9086/system/fpga_config/OrangeCrab_r0.2.1/fpga_top.v

631 lines
15 KiB
Coq
Raw Permalink Normal View History

/* 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 <http://www.gnu.org/licenses/>. */
`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;
/* verilator lint_off UNUSEDSIGNAL */
wire DISP_ERROR; //TODO: It would be ideal to acknoledge any warnings but it's not really too important
/* verilator lint_on UNUSEDSIGNAL */
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),
.IN1_ERROR(DISP_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