/* top.v - Implements FPGA and Board specific circuitry This file is part of the 9086 project. Copyright (c) 2024 Efthymios Kritikos This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ `include "error_header.v" `ifdef OUTPUT_JSON_STATISTICS `include "config.v" `endif module fpga_top( input clk48, input user_button, // output reset_n, output rgb_led0_r, output rgb_led0_g, output rgb_led0_b, `ifdef SYNTHESIS output wire [15:0] ddram_a, // [15:13] are unused in litex as well, they just also route them through a trellis block output wire [2:0] ddram_ba, output wire ddram_cas_n, output wire ddram_cke, output wire ddram_clk_p, output wire ddram_cs_n, output wire [1:0] ddram_dm, input wire [15:0] ddram_dq, input wire [1:0] ddram_dqs_p, output wire [1:0] ddram_gnd, output wire ddram_odt, output wire ddram_ras_n, output wire ddram_reset_n, output wire [5:0] ddram_vccio, output wire ddram_we_n, inout i2c_sda,/*sda*/ output i2c_scl /*scl*/ `else output i2c_dir, output i2c_scl, input i2c_sda_in, output i2c_sda_out `endif ); `ifndef SYNTHESIS string waveform_name; initial begin if($value$plusargs("WAVEFORM=%s",waveform_name))begin $dumpfile(waveform_name); $dumpvars(0,p,cycles); end end //TODO: should there be some common file between all the fpga_tops and system.v for this stuff? always @(posedge clk48) begin if(HALT==1&&disp_cache_start==disp_cache_end)begin $finish; end end reg sane; always @(posedge reset)begin sane<=1; end always @( ERROR ) begin if ( ERROR != `ERR_NO_ERROR && sane == 1 ) begin $display("PROCESSOR RUN INTO AN ERROR."); case (ERROR) default:begin end `ERR_UNIMPL_INSTRUCTION:begin $display("Unimplemented instruction"); end `ERR_UNIMPL_ADDRESSING_MODE: begin $display("Unimplemented addressing mode"); end endcase $finish; end end `endif `ifdef SYNTHESIS assign ddram_a[15:13] = 3'b0; assign ddram_vccio = 6'd63; assign ddram_gnd = 2'd0; `endif wire HALT; wire [`ERROR_BITS-1:0]ERROR; wire [19:0] address_bus; wire [15:0] data_bus_read,data_bus_write; wire rd,wr,BHE,IOMEM; wire CPU_SPEED; processor p( .clock(CPU_SPEED), .reset(reset), .HALT(HALT), .ERROR(ERROR), .external_address_bus(address_bus), .external_data_bus_read(data_bus_read), .external_data_bus_write(data_bus_write), .wait_state(wait_state), .read(rd), .write(wr), .BHE(BHE), .IOMEM(IOMEM) `ifdef CALCULATE_IPC /* STATISTICS */ ,.new_instruction(new_instruction) `endif `ifdef OUTPUT_JSON_STATISTICS /* */ ,.L1_SIZE_STAT(L1_SIZE_STAT), .VALID_INSTRUCTION_STAT(VALID_INSTRUCTION_STAT), .jump_req_debug(jump_req_debug) `endif ); /* verilator lint_off UNUSEDSIGNAL */ `ifdef CALCULATE_IPC wire new_instruction; `endif `ifdef OUTPUT_JSON_STATISTICS wire [`L1_CACHE_SIZE-1:0]L1_SIZE_STAT; wire VALID_INSTRUCTION_STAT; wire jump_req_debug; `endif /* verilator lint_on UNUSEDSIGNAL */ reg [2:0]rgb_led_color; assign rgb_led0_r=rgb_led_color[0]; assign rgb_led0_g=rgb_led_color[1]; assign rgb_led0_b=rgb_led_color[2]; // A bit useless since if the cpu ERRORS out or HALTS it will continue executing anyway //always @(HALT or ERROR or user_button) begin // if (HALT==1) begin // /* yellow */ // rgb_led_color<=3'b100; // end else if (ERROR != `ERROR_BITS'b0) begin // /* red */ // rgb_led_color<=3'b110; // end else begin // /* green */ // rgb_led_color<=3'b101; // end //end // Create a 27 bit register reg [26:0] counter = 0; // Every positive edge increment register by 1 always @(posedge clk48) begin counter <= counter + 1; end /*** RESET CIRCUIT ***/ reg reset=0; reg [1:0] state=0; always @(posedge counter[15]) begin if(user_button==0) state<=2'b00; case (state) 2'b00:begin reset<=0; state<=2'b01; end 2'b01:begin reset<=1; state<=2'b10; end default: begin end endcase end //------------------------------------------// // Cache to allow the slow display to have a // chance to keep up with the relentless CPU reg [7:0] disp_cache_start=0; reg [7:0] disp_cache_end=0; reg [7:0] disp_write_cache [255:0]; reg ascii_state=0; always @(posedge CPU_SPEED)begin if(wr==0)begin if(IOMEM==1'b1 && address_bus[7:0]==8'hA5 )begin disp_write_cache[disp_cache_end]<=data_bus_write[15:8]; disp_cache_end<=disp_cache_end+8'd1; end else if(IOMEM==1'b1 && address_bus[7:0]==8'hB0 )begin if(data_bus_write[0:0]==1) rgb_led_color<=3'b000; else rgb_led_color<=3'b111; end end else if(ascii_state==1'b0)begin if(ascii_data_ready&disp_cache_start!=disp_cache_end)begin `ifndef SYNTHESIS $write("%s" ,disp_write_cache[disp_cache_start]); // TODO: maybe simulate the i2c lcd `endif ascii_data<=disp_write_cache[disp_cache_start]; disp_cache_start<=disp_cache_start+8'd1; ascii_data_write_req<=1; ascii_state<=1'b1; end end if(ascii_state==1'b1)begin if(!ascii_data_ready)begin ascii_data_write_req<=0; ascii_state<=1'b0; end end end wire I2C_SPEED=counter[7]; wire [15:0]boot_rom_data_bus_read; wire [15:0]boot_rom_data_bus_write; assign boot_rom_data_bus_write=data_bus_write; wire boot_rom_cs_n; doublemem #( .RAM_SIZE_IN_BYTES(2048) ) boot_rom ( address_bus[10:0],boot_rom_data_bus_read,boot_rom_data_bus_write,rd,wr,BHE,boot_rom_cs_n,CPU_SPEED ); /// Memory Map assign data_bus_read = ( IOMEM == 1 ) ? data_bus_read_IO : data_bus_read_MEM ; wire [15:0] data_bus_read_IO = ( address_bus[7:1] == 7'h10 ) ? LITEDRAM_STATUS_REGISTER : ( (address_bus[7:4] == 4'h4) ? Wishbone_driver_data_bus_read:CPU_I2C_data_bus_read); assign boot_rom_cs_n = !((IOMEM==0)&&(address_bus[15:12]==4'hF)); wire [15:0] data_bus_read_MEM = (address_bus[15:12]==4'hF)? boot_rom_data_bus_read:data_bus_read_DDR3; wire DDR3_ram_cs = (IOMEM==0)&&(address_bus[15:12]!=4'hF); // // 0x20 | DDR STATUS BYTE1 | // 0x21 | DDR STATUS BYTE1 | // | | // - | -- | // | | // 0x30 | WISHBONE | // .. | .. | // 0x3F | WISHBONE | // assign wishbone_cs=!((IOMEM==1)&&(address_bus[7:4] == 4'h4)); assign i2c_cs= !((IOMEM==1)&&(address_bus[7:4] == 4'h6)); assign wait_state_I2C=(i2c_cs==1'b0&&rd==1'b0)^i2c_cs_1d; reg i2c_cs_1d; always @(posedge CPU_SPEED)begin i2c_cs_1d<=(i2c_cs==1'b0&&rd==1'b0); end // TODO: Probably would need to so the same for the wishbone IO wire wait_state_I2C; /// DDR3 Controller wire ddr3_init_done,ddr3_init_error,ddr3_pll_locked; /* verilator lint_off UNUSEDSIGNAL */ wire user_rst; /* verilator lint_on UNUSEDSIGNAL */ `ifndef SYNTHESIS assign ddr3_pll_locked=1; wire sim_trace; assign sim_trace=0;//signal is not connected on litedram, not sure what was the idea behind it `endif wire [15:0]Wishbone_driver_data_bus_read; wire [15:0]Wishbone_driver_data_bus_write=data_bus_write; wire wishbone_cs; Wishbone_IO_driver Wishbone_IO_driver( ///// GENERAL ////// .clock(CPU_SPEED), .reset_n(reset), ////// CPU INTERFACE /////// .address(address_bus[3:1]), .data_bus_in(Wishbone_driver_data_bus_write), .data_bus_out(Wishbone_driver_data_bus_read), .read_n(rd), .write_n(wr), .chip_select_n(wishbone_cs), ////// WISHBONE INTERFACE ///// .wb_ctrl_ack(wb_ctrl_ack), .wb_ctrl_adr(wb_ctrl_adr), .wb_ctrl_cyc(wb_ctrl_cyc), .wb_ctrl_err(wb_ctrl_err), .wb_ctrl_sel(wb_ctrl_sel), .wb_ctrl_stb(wb_ctrl_stb), .wb_ctrl_we(wb_ctrl_we), .wb_ctrl_bte(wb_ctrl_bte), .wb_ctrl_cti(wb_ctrl_cti), .wb_ctrl_dat_r(wb_ctrl_dat_r), .wb_ctrl_dat_w(wb_ctrl_dat_w) ); wire [15:0] LITEDRAM_STATUS_REGISTER = { 13'd0, ddr3_pll_locked ,ddr3_init_error, ddr3_init_done }; wire [15:0] data_bus_read_DDR3; Wishbone_memory_driver Wishbone_memory_driver( ///// GENERAL ////// .clock(CPU_SPEED), .reset_n(reset), ////// CPU INTERFACE /////// .address(address_bus), .BHE(BHE), .data_bus_in(data_bus_write), .data_bus_out(data_bus_read_DDR3), .wait_state(wait_state_WBIO), .read_n(rd), .write_n(wr), .chip_select_n(!(DDR3_ram_cs&&ddr3_init_done)), ////// WISHBONE INTERFACE ///// .wb_mem_ack(wb_mem_ack), .wb_mem_adr(wb_mem_adr), .wb_mem_cyc(wb_mem_cyc), .wb_mem_err(wb_mem_err), .wb_mem_sel(wb_mem_sel), .wb_mem_stb(wb_mem_stb), .wb_mem_we(wb_mem_we), .wb_mem_data_r(wb_mem_data_r), .wb_mem_data_w(wb_mem_data_w) ); wire wait_state; assign wait_state=wait_state_WBIO||wait_state_I2C; wire wait_state_WBIO; wire wb_ctrl_ack; wire [29:0] wb_ctrl_adr; wire [1:0] wb_ctrl_bte; wire [2:0] wb_ctrl_cti; wire wb_ctrl_cyc; wire [31:0] wb_ctrl_dat_r; wire [31:0] wb_ctrl_dat_w; wire wb_ctrl_err; wire [3:0] wb_ctrl_sel; wire wb_ctrl_stb; wire wb_ctrl_we; wire wb_mem_ack; wire [24:0] wb_mem_adr; wire wb_mem_cyc; wire [31:0] wb_mem_data_r; wire [31:0] wb_mem_data_w; wire wb_mem_err; wire [3:0] wb_mem_sel; wire wb_mem_stb; wire wb_mem_we; litedram_core DDR3_RAM_DRIVER( ////// GENERAL /////// .clk(clk48), .user_clk(CPU_SPEED), `ifdef SYNTHESIS .rst(!reset), .pll_locked(ddr3_pll_locked), `else .sim_trace(sim_trace), `endif ////// DDR3 INTERFACE ////// `ifdef SYNTHESIS .ddram_a(ddram_a[12:0]), //also ignored on the litedram core .ddram_ba(ddram_ba), .ddram_cas_n(ddram_cas_n), .ddram_cke(ddram_cke), .ddram_clk_n(1'b0), // goes nowhere ... .ddram_clk_p(ddram_clk_p), .ddram_cs_n(ddram_cs_n), .ddram_dm(ddram_dm), .ddram_dq(ddram_dq), .ddram_dqs_n(2'b00), // goes nowhere ... .ddram_dqs_p(ddram_dqs_p), .ddram_odt(ddram_odt), .ddram_ras_n(ddram_ras_n), .ddram_reset_n(ddram_reset_n), .ddram_we_n(ddram_we_n), `endif /////// SYSTEM MEMORY INTERFACE //////////////// .init_done(ddr3_init_done), .init_error(ddr3_init_error), .user_port_wishbone_0_ack(wb_mem_ack), .user_port_wishbone_0_adr(wb_mem_adr), .user_port_wishbone_0_cyc(wb_mem_cyc), .user_port_wishbone_0_dat_r(wb_mem_data_r), .user_port_wishbone_0_dat_w(wb_mem_data_w), .user_port_wishbone_0_err(wb_mem_err), .user_port_wishbone_0_sel(wb_mem_sel), .user_port_wishbone_0_stb(wb_mem_stb), .user_port_wishbone_0_we(wb_mem_we), .user_rst(user_rst), /////// WISHBONE CONTROL INTERFACE /////////// .wb_ctrl_ack(wb_ctrl_ack), .wb_ctrl_adr(wb_ctrl_adr), .wb_ctrl_cyc(wb_ctrl_cyc), .wb_ctrl_err(wb_ctrl_err), .wb_ctrl_sel(wb_ctrl_sel), .wb_ctrl_stb(wb_ctrl_stb), .wb_ctrl_we(wb_ctrl_we), .wb_ctrl_bte(wb_ctrl_bte), .wb_ctrl_cti(wb_ctrl_cti), .wb_ctrl_dat_r(wb_ctrl_dat_r), .wb_ctrl_dat_w(wb_ctrl_dat_w) ); wire [15:0]CPU_I2C_data_bus_read; wire i2c_cs; wire [6:0] CPU_I2C_OUT_ADDRESS; wire CPU_I2C_OUT_BUSY,CPU_I2C_OUT_TRANSACT,CPU_I2C_DIR; wire [15:0]CPU_I2C_DATA_READ,CPU_I2C_DATA_WRITE; wire CPU_I2C_IGN_ACK; wire CPU_I2C_ERROR; CPU_to_I2C_driver_bridge CPU_to_I2C_driver_bridge ( .clock(CPU_SPEED), .reset_n(reset), // CPU INTERFACE .address(address_bus[2:0]), .data_bus_in(data_bus_write), .data_bus_out(CPU_I2C_data_bus_read), .read_n(rd), .write_n(wr), .chip_select_n(i2c_cs), // I2C DRIVER INTERFACE .OUT_ADDRESS(CPU_I2C_OUT_ADDRESS), .OUT_BUSY(CPU_I2C_OUT_BUSY), .OUT_TRANSACT(CPU_I2C_OUT_TRANSACT), .DIR(CPU_I2C_DIR), .OUT_I2C_DATA_READ(CPU_I2C_DATA_READ), .OUT_I2C_DATA_WRITE(CPU_I2C_DATA_WRITE), .TRANS_WIDTH(CPU_I2C_TRANS_WIDTH), .OUT_IGN_ACK(CPU_I2C_IGN_ACK), .IN_ERROR(CPU_I2C_ERROR) ); // Display driver wire ascii_data_ready; reg ascii_data_write_req=0; reg [7:0] ascii_data; ascii_to_HD44780_driver LCD_DRIVER( /* system */ I2C_SPEED, 1'b1, /* Data Input */ ascii_data_ready, ascii_data_write_req, ascii_data, /* write circuitry */ !pcf_busy, pcf_write_req, pcf_data, pcf_command_data ); // Port expander driver wire pcf_write_req,pcf_command_data,pcf_busy; wire [3:0]pcf_data; /* verilator lint_off UNUSEDSIGNAL */ wire [7:0]DISP_I2C_DATA_WRITE; wire [15:0]DISP_I2C_DATA_READ; /* verilator lint_on UNUSEDSIGNAL */ wire DISP_I2C_BUSY,DISP_I2C_TRANSACT; wire [6:0] DISP_I2C_ADDRESS; assign DISP_I2C_ADDRESS=7'h27; wire DISP_DIR; pcf8574_for_HD44780 PCF8574_driver( .clock(I2C_SPEED), .pcf_write_req(pcf_write_req), .pcf_command_data(pcf_command_data), .pcf_data(pcf_data), .pcf_busy(pcf_busy), .new_backlight(1'b0), .backlight_update(1'b0), .DIR(DISP_DIR), .I2C_BUSY(DISP_I2C_BUSY), .I2C_TRANSACT(DISP_I2C_TRANSACT), .i2c_data_write(DISP_I2C_DATA_WRITE) ); wire [6:0]MULT_TO_DRIV_I2C_ADDRESS; wire MULT_TO_DRIV_I2C_BUSY; wire MULT_TO_DRIV_I2C_TRANSACT; wire [15:0]MULT_TO_DRIV_DATA_WRITE, MULT_TO_DRIV_DATA_READ; wire MULT_TO_DRIV_DIR; wire CPU_I2C_TRANS_WIDTH; I2C_driver_multiplexer I2C_driver_multiplexer( .clock(I2C_SPEED), .reset_n(reset), ////// INPUT 1 /////// .IN1_ADDRESS(DISP_I2C_ADDRESS), .IN1_BUSY(DISP_I2C_BUSY), .IN1_DIR(DISP_DIR), .IN1_TRANSACT(DISP_I2C_TRANSACT), .IN1_I2C_DATA_READ(DISP_I2C_DATA_READ), .IN1_I2C_DATA_WRITE({8'h0,DISP_I2C_DATA_WRITE}), .IN1_TRANS_WIDTH(1'b0), .IN1_IGN_ACK(1'b0), //.IN2_ERROR(), ////// INPUT 2 /////// .IN2_ADDRESS(CPU_I2C_OUT_ADDRESS), .IN2_BUSY(CPU_I2C_OUT_BUSY), .IN2_TRANSACT(CPU_I2C_OUT_TRANSACT), .IN2_DIR(CPU_I2C_DIR), .IN2_I2C_DATA_READ(CPU_I2C_DATA_READ), .IN2_I2C_DATA_WRITE(CPU_I2C_DATA_WRITE), .IN2_TRANS_WIDTH(CPU_I2C_TRANS_WIDTH), .IN2_IGN_ACK(CPU_I2C_IGN_ACK), .IN2_ERROR(CPU_I2C_ERROR), ////// OUTPUT /////// .OUT_ADDRESS(MULT_TO_DRIV_I2C_ADDRESS), .OUT_BUSY(MULT_TO_DRIV_I2C_BUSY), .OUT_TRANSACT(MULT_TO_DRIV_I2C_TRANSACT), .OUT_DIR(MULT_TO_DRIV_DIR), .OUT_I2C_DATA_WRITE(MULT_TO_DRIV_DATA_WRITE), .OUT_I2C_DATA_READ(MULT_TO_DRIV_DATA_READ), .OUT_TRANS_WIDTH(MULT_TO_DRIV_TRANS_WIDTH), .OUT_IGN_ACK(MULT_TO_DRIV_IGN_ACK), .OUT_ERROR(MULT_TO_DRIV_ERROR) ); // I2C driver wire SDA_direction; wire SCL,SDA_input,SDA_output; wire MULT_TO_DRIV_TRANS_WIDTH; wire MULT_TO_DRIV_IGN_ACK; wire MULT_TO_DRIV_ERROR; I2C_driver i2c_driver( .clock(I2C_SPEED), .SDA_input(SDA_input), .SDA_output(SDA_output), .SDA_direction(SDA_direction), .SCL(SCL), .address(MULT_TO_DRIV_I2C_ADDRESS), .I2C_BUSY(MULT_TO_DRIV_I2C_BUSY), .I2C_TRANSACT(MULT_TO_DRIV_I2C_TRANSACT), .DIR(MULT_TO_DRIV_DIR), .i2c_data_write(MULT_TO_DRIV_DATA_WRITE), .i2c_data_read(MULT_TO_DRIV_DATA_READ), .transact_width(MULT_TO_DRIV_TRANS_WIDTH), .ignore_ack(MULT_TO_DRIV_IGN_ACK), .error(MULT_TO_DRIV_ERROR) ); `ifdef SYNTHESIS TRELLIS_IO #( // Parameters. .DIR ("BIDIR") ) TRELLIS_IO_00 ( // pin .B (i2c_sda), //input .I (1'd0), //Direction .T (~( SDA_direction & (~SDA_output) )), // Output .O (SDA_input) ); assign i2c_scl=SCL; `else assign i2c_dir=SDA_direction; assign i2c_scl=SCL; assign SDA_input=i2c_sda_in; assign i2c_sda_out=SDA_output; `endif endmodule