From b845507e32d5d8e6ec43e4a760359096b074ec11 Mon Sep 17 00:00:00 2001 From: hakan-demirli <78746991+hakan-demirli@users.noreply.github.com> Date: Thu, 15 Jul 2021 18:02:26 +0300 Subject: [PATCH 001/157] Update serv_timer.c Fix a typo 'fro' --- zephyr/drivers/timer/serv_timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zephyr/drivers/timer/serv_timer.c b/zephyr/drivers/timer/serv_timer.c index e7474fe..0eec6ee 100644 --- a/zephyr/drivers/timer/serv_timer.c +++ b/zephyr/drivers/timer/serv_timer.c @@ -5,7 +5,7 @@ */ /* - This is basically a 32-bit version of riscv_machine_timer.c fro Zephyr + This is basically a 32-bit version of riscv_machine_timer.c for Zephyr */ #include #include From dbe5236b4c351632403865aa295ce07e895ded0c Mon Sep 17 00:00:00 2001 From: Dhiru Kholia Date: Thu, 12 Aug 2021 22:04:09 +0530 Subject: [PATCH 002/157] Add support for EBAZ4205 'Development' Board References: - https://github.com/fusesoc/blinky/pull/68/files (EBAZ4205 blinky) - https://github.com/fusesoc/blinky#ebaz4205-development-board - Existing 'arty_a7_35t' example This PR also cleans up a bunch of whitespace issues (no functional change). --- README.md | 19 +++++++++++--- data/ebaz4205.xdc | 10 ++++++++ servant.core | 17 +++++++++++- servant/servix_ebaz4205.v | 30 ++++++++++++++++++++++ servant/servix_ebaz4205_clock_gen.v | 40 +++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 data/ebaz4205.xdc create mode 100644 servant/servix_ebaz4205.v create mode 100644 servant/servix_ebaz4205_clock_gen.v diff --git a/README.md b/README.md index 0f9dcbd..186a1db 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The FuseSoC standard library already contain a version of SERV, but if we want t `fusesoc library add serv https://github.com/olofk/serv` -The SERV repo will now be available in $WORKSPACE/fusesoc_libraries/serv. To save some typing, we will refer to that directory as `$SERV`. +The SERV repo will now be available in $WORKSPACE/fusesoc_libraries/serv. To save some typing, we will refer to that directory as `$SERV`. We are now ready to do our first exercises with SERV @@ -45,7 +45,7 @@ If everything worked, the output should look like INFO: Preparing ::serv:1.1.0 INFO: Setting up project - + INFO: Building simulation model INFO: Running @@ -159,13 +159,24 @@ FPGA Pin Y15 (Connector JP7, pin 1) is used for UART output with 57600 baud rate ### DECA development kit -FPGA Pin W18 (Pin 3 P8 connector) is used for UART output with 57600 baud rate. Key 0 is reset and Led 0 q output. +FPGA Pin W18 (Pin 3 P8 connector) is used for UART output with 57600 baud rate. Key 0 is reset and Led 0 q output. fusesoc run --target=deca servant +### EBAZ4205 'Development' Board + +Pin B20 is used for UART output with 57600 baud rate. To use `blinky.hex` +change B20 to W14 (red led) in `data/ebaz4205.xdc` file). + + fusesoc run --target=ebaz4205 servant + + fusesoc run --target=ebaz4205 servant --memfile=$SERV/sw/blinky.hex + +Reference: https://github.com/fusesoc/blinky#ebaz4205-development-board + ### SoCKit development kit -FPGA Pin F14 (HSTC GPIO addon connector J2, pin 2) is used for UART output with 57600 baud rate. +FPGA Pin F14 (HSTC GPIO addon connector J2, pin 2) is used for UART output with 57600 baud rate. fusesoc run --target=sockit servant diff --git a/data/ebaz4205.xdc b/data/ebaz4205.xdc new file mode 100644 index 0000000..15219c1 --- /dev/null +++ b/data/ebaz4205.xdc @@ -0,0 +1,10 @@ +## 33.333 MHz Clock signal +set_property -dict { PACKAGE_PIN N18 IOSTANDARD LVCMOS33 } [get_ports i_clk]; +create_clock -add -name sys_clk_pin -period 30.00 -waveform {0 5} [get_ports i_clk]; + +## LED(s) +# set_property -dict { PACKAGE_PIN W13 IOSTANDARD LVCMOS33 } [get_ports { q_green }]; +# set_property -dict { PACKAGE_PIN W14 IOSTANDARD LVCMOS33 } [get_ports { q }]; + +## UART on DATA1_8 pin +set_property -dict { PACKAGE_PIN B20 IOSTANDARD LVCMOS33 } [get_ports q]; diff --git a/servant.core b/servant.core index 5bf3636..938b2df 100644 --- a/servant.core +++ b/servant.core @@ -76,6 +76,12 @@ filesets: - servant/servive_clock_gen.v : {file_type : verilogSource} - servant/servive.v : {file_type : verilogSource} + ebaz4205: + files: + - servant/servix_ebaz4205_clock_gen.v : {file_type : verilogSource} + - servant/servix_ebaz4205.v : {file_type : verilogSource} + - data/ebaz4205.xdc : {file_type : xdc} + tinyfpga_bx: {files: [data/tinyfpga_bx.pcf : {file_type : PCF}]} icebreaker : {files: [data/icebreaker.pcf : {file_type : PCF}]} icesugar : {files: [data/icesugar.pcf : {file_type : PCF}]} @@ -182,7 +188,7 @@ targets: family : MAX 10 device : 10M50DAF484C6GES toplevel: servive - + de0_nano: default_tool : quartus filesets : [mem_files, soc, de0_nano] @@ -296,6 +302,15 @@ targets: vivado: {part : xc7a35ticsg324-1L} toplevel : servix + ebaz4205: + default_tool: vivado + description: EBAZ4205 'Development' Board + filesets : [mem_files, soc, ebaz4205] + parameters : [memfile, memsize, frequency=16] + tools: + vivado: {part : xc7z010clg400-1} + toplevel : servix_ebaz4205 + ac701: default_tool: vivado filesets : [mem_files, soc, ac701] diff --git a/servant/servix_ebaz4205.v b/servant/servix_ebaz4205.v new file mode 100644 index 0000000..1819c37 --- /dev/null +++ b/servant/servix_ebaz4205.v @@ -0,0 +1,30 @@ +`default_nettype none +module servix_ebaz4205 +( + input wire i_clk, + output wire q); + + parameter frequency = 32; + parameter memfile = "zephyr_hello.hex"; + parameter memsize = 8192; + parameter PLL = "NONE"; + + wire wb_clk; + wire wb_rst; + + servix_ebaz4205_clock_gen + #(.frequency (frequency)) + clock_gen + (.i_clk (i_clk), + .o_clk (wb_clk), + .o_rst (wb_rst)); + + servant + #(.memfile (memfile), + .memsize (memsize)) + servant + (.wb_clk (wb_clk), + .wb_rst (wb_rst), + .q (q)); + +endmodule diff --git a/servant/servix_ebaz4205_clock_gen.v b/servant/servix_ebaz4205_clock_gen.v new file mode 100644 index 0000000..54984a4 --- /dev/null +++ b/servant/servix_ebaz4205_clock_gen.v @@ -0,0 +1,40 @@ +`default_nettype none +module servix_ebaz4205_clock_gen + (input wire i_clk, + output wire o_clk, + output reg o_rst); + + parameter frequency = 32; + + wire clkfb; + wire locked; + reg locked_r; + + // (33.333 * 48) / 50 => 31.9996 MHz + PLLE2_BASE + #(.BANDWIDTH("OPTIMIZED"), + .CLKFBOUT_MULT(48), + .CLKIN1_PERIOD(30.000300003), // 33.333 MHz + .CLKOUT0_DIVIDE((frequency == 32) ? 50 : 100), + .DIVCLK_DIVIDE(1), + .STARTUP_WAIT("FALSE")) + PLLE2_BASE_inst + (.CLKOUT0(o_clk), + .CLKOUT1(), + .CLKOUT2(), + .CLKOUT3(), + .CLKOUT4(), + .CLKOUT5(), + .CLKFBOUT(clkfb), + .LOCKED(locked), + .CLKIN1(i_clk), + .PWRDWN(1'b0), + .RST(1'b0), + .CLKFBIN(clkfb)); + + always @(posedge o_clk) begin + locked_r <= locked; + o_rst <= !locked_r; + end + +endmodule From 6e802cb9bc19cefeae5d7e60d75bd6b9edb7230a Mon Sep 17 00:00:00 2001 From: Zeeshan Rafique <36025181+zeeshanrafique23@users.noreply.github.com> Date: Sat, 21 Aug 2021 02:31:42 +0500 Subject: [PATCH 003/157] M-extension support for SERV * modified serv(ant) for MDU * added dependency for mdu * M-extension for SERV * Updated README for running RV32IM compliance tests * waive some lint warnings related to mdu * added mdu param for arty_a7_35t --- README.md | 2 +- data/verilator_waiver.vlt | 12 +++++++- rtl/serv_bufreg.v | 24 +++++++++++----- rtl/serv_decode.v | 52 ++++++++++++++++++++++++++--------- rtl/serv_mem_if.v | 17 ++++++++++-- rtl/serv_rf_top.v | 32 +++++++++++++++++---- rtl/serv_state.v | 47 +++++++++++++++++++++++-------- rtl/serv_top.v | 58 +++++++++++++++++++++++++++++++++------ serv.core | 6 ++++ servant.core | 11 ++++++-- servant/servant.v | 37 ++++++++++++++++++++++++- 11 files changed, 246 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index 186a1db..8845db7 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Run the compliance tests cd riscv-compliance && make TARGETDIR=$SERV/riscv-target RISCV_TARGET=serv RISCV_DEVICE=rv32i RISCV_ISA=rv32i TARGET_SIM=$WORKSPACE/build/servant_1.1.0/verilator_tb-verilator/Vservant_sim -The above will run all tests in the rv32i test suite. Since SERV also implement the `rv32Zicsr` and `rv32Zifencei` extensions, these can also be tested by choosing any of them instead of rv32i as the `RISCV_ISA` variable. +The above will run all tests in the rv32i test suite. Since SERV also implement the `rv32im`, `rv32Zicsr` and `rv32Zifencei` extensions, these can also be tested by choosing any of them instead of rv32i as the `RISCV_ISA` variable. ## Run on hardware diff --git a/data/verilator_waiver.vlt b/data/verilator_waiver.vlt index 218fd16..b3a94e7 100644 --- a/data/verilator_waiver.vlt +++ b/data/verilator_waiver.vlt @@ -1,7 +1,17 @@ `verilator_config // Bits [1:0] in i_ibus_rdt are not used at all -lint_off -rule UNUSED -file "*/serv_top.v" -lines 52 +lint_off -rule UNUSED -file "*/serv_top.v" -lines 53 //Some bits in the instruction word are not used in serv_decode but it's easier //to just send in the whole word than picking out bits lint_off -rule UNUSED -file "*/serv_decode.v" -lines 7 + +//Some variables are only used when we connect an Extension with serv_decode +lint_off -rule UNUSED -file "*/serv_top.v" -lines 65 +lint_off -rule UNUSED -file "*/serv_bufreg.v" -lines 10 +lint_off -rule UNUSED -file "*/serv_decode.v" -lines 8 +lint_off -rule UNUSED -file "*/serv_decode.v" -lines 69 +lint_off -rule UNUSED -file "*/serv_mem_if.v" -lines 23 +lint_off -rule UNUSED -file "*/serv_mem_if.v" -lines 71 +lint_off -rule UNUSED -file "*/serv_state.v" -lines 47 +lint_off -rule UNUSED -file "*/serv_state.v" -lines 49 diff --git a/rtl/serv_bufreg.v b/rtl/serv_bufreg.v index 7d32b42..dcf2354 100644 --- a/rtl/serv_bufreg.v +++ b/rtl/serv_bufreg.v @@ -1,12 +1,14 @@ -module serv_bufreg - ( +module serv_bufreg #( + parameter [0:0] MDU = 0 +)( input wire i_clk, //State input wire i_cnt0, input wire i_cnt1, input wire i_en, input wire i_init, - output reg [1:0] o_lsb, + input wire i_mdu_op, + output wire [1:0] o_lsb, //Control input wire i_rs1_en, input wire i_imm_en, @@ -17,11 +19,14 @@ module serv_bufreg input wire i_imm, output wire o_q, //External - output wire [31:0] o_dbus_adr); + output wire [31:0] o_dbus_adr, + //Extension + output wire [31:0] o_ext_rs1); wire c, q; reg c_r; reg [31:2] data; + reg [1:0] lsb; wire clr_lsb = i_cnt0 & i_clr_lsb; @@ -35,11 +40,16 @@ module serv_bufreg data <= {i_init ? q : (data[31] & i_sh_signed), data[31:3]}; if (i_init ? (i_cnt0 | i_cnt1) : i_en) - o_lsb <= {i_init ? q : data[2],o_lsb[1]}; - + lsb <= {i_init ? q : data[2],lsb[1]}; end - assign o_q = o_lsb[0] & i_en; + assign o_q = lsb[0] & i_en; assign o_dbus_adr = {data, 2'b00}; + assign o_ext_rs1 = {o_dbus_adr[31:2],lsb}; + + generate + if (MDU) assign o_lsb = i_mdu_op ? 2'b00 : lsb; + else assign o_lsb = lsb; + endgenerate endmodule diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index b675901..f4eb7cf 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -1,7 +1,8 @@ `default_nettype none -module serv_decode #( - parameter [0:0] PRE_REGISTER = 1 -)( +module serv_decode + #(parameter [0:0] PRE_REGISTER = 1, + parameter [0:0] MDU = 0) + ( input wire clk, //Input input wire [31:2] i_wb_rdt, @@ -17,6 +18,10 @@ module serv_decode #( output reg o_shift_op, output reg o_slt_op, output reg o_rd_op, + //MDU + output reg o_mdu_op, + //Extension + output reg [2:0] o_ext_funct3, //To bufreg output reg o_bufreg_rs1_en, output reg o_bufreg_imm_en, @@ -61,8 +66,33 @@ module serv_decode #( reg op22; reg op26; + reg imm25; reg imm30; +generate + wire co_mdu_op; + wire [2:0]co_ext_funct3; + wire co_shift_op; + wire co_slt_op; + wire co_mem_word; + wire co_rd_alu_en; + + if (MDU) begin + assign co_mdu_op = ((opcode == 5'b01100) & imm25); + assign co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; + assign co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01) & !co_mdu_op; + assign co_mem_word = co_mdu_op ? co_mdu_op :funct3[1]; + assign co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4] & !co_mdu_op; + end else begin + assign co_mdu_op = 1'b0; + assign co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01); + assign co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01); + assign co_mem_word = funct3[1]; + assign co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4]; + end + assign co_ext_funct3 = funct3; +endgenerate + //opcode wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); @@ -109,13 +139,6 @@ module serv_decode #( wire co_sh_right = funct3[2]; wire co_bne_or_bge = funct3[0]; - // - // opcode & funct3 - // - - wire co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01); - wire co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01); - //Matches system ops except eceall/ebreak/mret wire csr_op = opcode[4] & opcode[2] & (|funct3); @@ -190,7 +213,6 @@ module serv_decode #( wire co_mem_cmd = opcode[3]; wire co_mem_signed = ~funct3[2]; - wire co_mem_word = funct3[1]; wire co_mem_half = funct3[0]; wire [1:0] co_alu_bool_op = funct3[1:0]; @@ -220,8 +242,6 @@ module serv_decode #( //1 (OP_B_SOURCE_RS2) when BRANCH or OP wire co_op_b_source = opcode[3]; - wire co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4]; - generate if (PRE_REGISTER) begin @@ -229,6 +249,7 @@ module serv_decode #( if (i_wb_en) begin funct3 <= i_wb_rdt[14:12]; imm30 <= i_wb_rdt[30]; + imm25 <= i_wb_rdt[25]; opcode <= i_wb_rdt[6:2]; op20 <= i_wb_rdt[20]; op21 <= i_wb_rdt[21]; @@ -248,6 +269,8 @@ module serv_decode #( o_shift_op = co_shift_op; o_slt_op = co_slt_op; o_rd_op = co_rd_op; + o_mdu_op = co_mdu_op; + o_ext_funct3 = co_ext_funct3; o_bufreg_rs1_en = co_bufreg_rs1_en; o_bufreg_imm_en = co_bufreg_imm_en; o_bufreg_clr_lsb = co_bufreg_clr_lsb; @@ -285,6 +308,7 @@ module serv_decode #( always @(*) begin funct3 = i_wb_rdt[14:12]; imm30 = i_wb_rdt[30]; + imm25 = i_wb_rdt[25]; opcode = i_wb_rdt[6:2]; op20 = i_wb_rdt[20]; op21 = i_wb_rdt[21]; @@ -304,6 +328,8 @@ module serv_decode #( o_shift_op <= co_shift_op; o_slt_op <= co_slt_op; o_rd_op <= co_rd_op; + o_mdu_op <= co_mdu_op; + o_ext_funct3 <= co_ext_funct3; o_bufreg_rs1_en <= co_bufreg_rs1_en; o_bufreg_imm_en <= co_bufreg_imm_en; o_bufreg_clr_lsb <= co_bufreg_clr_lsb; diff --git a/rtl/serv_mem_if.v b/rtl/serv_mem_if.v index fbdc3c4..ee688ac 100644 --- a/rtl/serv_mem_if.v +++ b/rtl/serv_mem_if.v @@ -1,6 +1,7 @@ `default_nettype none module serv_mem_if - #(parameter WITH_CSR = 1) + #(parameter WITH_CSR = 1, + parameter [0:0] MDU = 0) ( input wire i_clk, //State @@ -18,6 +19,8 @@ module serv_mem_if input wire i_signed, input wire i_word, input wire i_half, + //MDU + input wire i_mdu_op, //Data input wire i_op_b, output wire o_rd, @@ -58,7 +61,17 @@ module serv_mem_if (i_bytecnt == 2'b00) | (i_half & !i_bytecnt[1]); - assign o_rd = i_mem_op & (dat_valid ? dat_cur : signbit & i_signed); + wire mem_rd = i_mem_op & (dat_valid ? dat_cur : signbit & i_signed); + + generate + if(MDU) begin + wire mdu_rd = i_mdu_op & dat_cur; + assign o_rd = mem_rd | mdu_rd; + end else begin + wire mdu_rd = 1'b0; + assign o_rd = mem_rd; + end + endgenerate assign o_wb_sel[3] = (i_lsb == 2'b11) | i_word | (i_half & i_lsb[1]); assign o_wb_sel[2] = (i_lsb == 2'b10) | i_word; diff --git a/rtl/serv_rf_top.v b/rtl/serv_rf_top.v index 3f41373..6dab9f7 100644 --- a/rtl/serv_rf_top.v +++ b/rtl/serv_rf_top.v @@ -2,7 +2,10 @@ module serv_rf_top #(parameter RESET_PC = 32'd0, - + /* Multiplication and Division Unit + This parameter enables the interface for connecting SERV and MDU + */ + parameter [0:0] MDU = 0, /* Register signals before or after the decoder 0 : Register after the decoder. Faster but uses more resources 1 : (default) Register before the decoder. Slower but uses less resources @@ -55,8 +58,17 @@ module serv_rf_top output wire o_dbus_we , output wire o_dbus_cyc, input wire [31:0] i_dbus_rdt, - input wire i_dbus_ack); - + input wire i_dbus_ack, + + // Extension + output wire [31:0] o_ext_rs1, + output wire [31:0] o_ext_rs2, + output wire [ 2:0] o_ext_funct3, + input wire [31:0] i_ext_rd, + input wire i_ext_ready, + // MDU + output wire o_mdu_valid); + localparam CSR_REGS = WITH_CSR*4; wire rf_wreq; @@ -120,7 +132,8 @@ module serv_rf_top #(.RESET_PC (RESET_PC), .PRE_REGISTER (PRE_REGISTER), .RESET_STRATEGY (RESET_STRATEGY), - .WITH_CSR (WITH_CSR)) + .WITH_CSR (WITH_CSR), + .MDU(MDU)) cpu ( .clk (clk), @@ -174,7 +187,16 @@ module serv_rf_top .o_dbus_we (o_dbus_we), .o_dbus_cyc (o_dbus_cyc), .i_dbus_rdt (i_dbus_rdt), - .i_dbus_ack (i_dbus_ack)); + .i_dbus_ack (i_dbus_ack), + + //Extension + .o_ext_funct3 (o_ext_funct3), + .i_ext_ready (i_ext_ready), + .i_ext_rd (i_ext_rd), + .o_ext_rs1 (o_ext_rs1), + .o_ext_rs2 (o_ext_rs2), + //MDU + .o_mdu_valid (o_mdu_valid)); endmodule `default_nettype wire diff --git a/rtl/serv_state.v b/rtl/serv_state.v index 7fa088a..dfe2ff8 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -1,6 +1,7 @@ module serv_state #(parameter RESET_STRATEGY = "MINI", - parameter [0:0] WITH_CSR = 1) + parameter [0:0] WITH_CSR = 1, + parameter [0:0] MDU = 0) ( input wire i_clk, input wire i_rst, @@ -41,11 +42,16 @@ module serv_state output wire [1:0] o_mem_bytecnt, input wire i_mem_misalign, output reg o_cnt_done, - output wire o_bufreg_en); + output wire o_bufreg_en, + //MDU + input wire i_mdu_op, + output wire o_mdu_valid, + input wire i_mdu_ready); reg stage_two_req; reg init_done; wire misalign_trap_sync; + wire two_stage_op; reg [4:2] o_cnt; reg [3:0] o_cnt_r; @@ -74,8 +80,34 @@ module serv_state //been calculated. wire take_branch = i_branch_op & (!i_cond_branch | (i_alu_cmp^i_bne_or_bge)); - //slt*, branch/jump, shift, load/store - wire two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op; +generate + if (MDU) begin + //slt*, branch/jump, shift, load/store + assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op | i_mdu_op; + + //valid signal for mdu + assign o_mdu_valid = !o_cnt_en & init_done & i_mdu_op; + + //Prepare RF for writes when everything is ready to enter stage two + // and the first stage didn't cause a misalign exception + assign o_rf_wreq = !misalign_trap_sync & + ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | + (i_mem_op & i_dbus_ack) | i_mdu_ready | + (stage_two_req & (i_slt_op | i_branch_op))); + end else begin + //slt*, branch/jump, shift, load/store + assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op; + + //valid signal for mdu turned-off + assign o_mdu_valid = 1'b0; + + //Prepare RF for writes when everything is ready to enter stage two + // and the first stage didn't cause a misalign exception + assign o_rf_wreq = !misalign_trap_sync & + ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | + (i_mem_op & i_dbus_ack) | (stage_two_req & (i_slt_op | i_branch_op))); + end +endgenerate assign o_dbus_cyc = !o_cnt_en & init_done & i_mem_op & !i_mem_misalign; @@ -83,13 +115,6 @@ module serv_state // or when stage one caused an exception (rreq implies a write request too) assign o_rf_rreq = i_ibus_ack | (stage_two_req & misalign_trap_sync); - //Prepare RF for writes when everything is ready to enter stage two - // and the first stage didn't cause a misalign exception - assign o_rf_wreq = !misalign_trap_sync & - ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | - (i_mem_op & i_dbus_ack) | - (stage_two_req & (i_slt_op | i_branch_op))); - assign o_rf_rd_en = i_rd_op & !o_init; /* diff --git a/rtl/serv_top.v b/rtl/serv_top.v index c465ae4..b517b2e 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -4,7 +4,8 @@ module serv_top #(parameter WITH_CSR = 1, parameter PRE_REGISTER = 1, parameter RESET_STRATEGY = "MINI", - parameter RESET_PC = 32'd0) + parameter RESET_PC = 32'd0, + parameter [0:0] MDU = 1'b0) ( input wire clk, input wire i_rst, @@ -57,7 +58,15 @@ module serv_top output wire o_dbus_we , output wire o_dbus_cyc, input wire [31:0] i_dbus_rdt, - input wire i_dbus_ack); + input wire i_dbus_ack, + //Extension + output wire [ 2:0] o_ext_funct3, + input wire i_ext_ready, + input wire [31:0] i_ext_rd, + output wire [31:0] o_ext_rs1, + output wire [31:0] o_ext_rs2, + //MDU + output wire o_mdu_valid); wire [4:0] rd_addr; wire [4:0] rs1_addr; @@ -76,6 +85,7 @@ module serv_top wire shift_op; wire slt_op; wire rd_op; + wire mdu_op; wire rd_alu_en; wire rd_csr_en; @@ -157,7 +167,8 @@ module serv_top serv_state #(.RESET_STRATEGY (RESET_STRATEGY), - .WITH_CSR (WITH_CSR)) + .WITH_CSR (WITH_CSR), + .MDU(MDU)) state ( .i_clk (clk), @@ -194,6 +205,11 @@ module serv_top .i_slt_op (slt_op), .i_e_op (e_op), .i_rd_op (rd_op), + //MDU + .i_mdu_op (mdu_op), + .o_mdu_valid (o_mdu_valid), + //Extension + .i_mdu_ready (i_ext_ready), //External .o_dbus_cyc (o_dbus_cyc), .i_dbus_ack (i_dbus_ack), @@ -206,7 +222,8 @@ module serv_top .o_rf_rd_en (rd_en)); serv_decode - #(.PRE_REGISTER (PRE_REGISTER)) + #(.PRE_REGISTER (PRE_REGISTER), + .MDU(MDU)) decode ( .clk (clk), @@ -224,6 +241,10 @@ module serv_top .o_slt_op (slt_op), .o_rd_op (rd_op), .o_sh_right (sh_right), + .o_mdu_op (mdu_op), + //Extension + .o_ext_funct3 (o_ext_funct3), + //To bufreg .o_bufreg_rs1_en (bufreg_rs1_en), .o_bufreg_imm_en (bufreg_imm_en), @@ -281,7 +302,9 @@ module serv_top .i_wb_en (i_ibus_ack), .i_wb_rdt (i_ibus_rdt[31:7])); - serv_bufreg bufreg + serv_bufreg + #(.MDU(MDU)) + bufreg ( .i_clk (clk), //State @@ -289,6 +312,7 @@ module serv_top .i_cnt1 (cnt1), .i_en (bufreg_en), .i_init (init), + .i_mdu_op (mdu_op), .o_lsb (lsb), //Control .i_sh_signed (bufreg_sh_signed), @@ -300,7 +324,8 @@ module serv_top .i_imm (imm), .o_q (bufreg_q), //External - .o_dbus_adr (o_dbus_adr)); + .o_dbus_adr (o_dbus_adr), + .o_ext_rs1 (o_ext_rs1)); serv_ctrl #(.RESET_PC (RESET_PC), @@ -398,7 +423,8 @@ module serv_top .o_csr (rf_csr_out)); serv_mem_if - #(.WITH_CSR (WITH_CSR)) + #(.WITH_CSR (WITH_CSR), + .MDU(MDU)) mem_if ( .i_clk (clk), @@ -412,6 +438,7 @@ module serv_top .o_sh_done (mem_sh_done), .o_sh_done_r (mem_sh_done_r), //Control + .i_mdu_op (mdu_op), .i_mem_op (mem_op), .i_shift_op (shift_op), .i_signed (mem_signed), @@ -423,8 +450,8 @@ module serv_top //External interface .o_wb_dat (o_dbus_dat), .o_wb_sel (o_dbus_sel), - .i_wb_rdt (i_dbus_rdt), - .i_wb_ack (i_dbus_ack)); + .i_wb_rdt (dbus_rdt), + .i_wb_ack (dbus_ack)); generate if (WITH_CSR) begin @@ -525,5 +552,18 @@ module serv_top `endif +generate + wire [31:0] dbus_rdt; + wire dbus_ack; + if (MDU) begin + assign dbus_rdt = i_ext_ready ? i_ext_rd:i_dbus_rdt; + assign dbus_ack = i_dbus_ack | i_ext_ready; + end else begin + assign dbus_rdt = i_dbus_rdt; + assign dbus_ack = i_dbus_ack; + end + assign o_ext_rs2 = o_dbus_dat; +endgenerate + endmodule `default_nettype wire diff --git a/serv.core b/serv.core index 7163ebc..a0cbe14 100644 --- a/serv.core +++ b/serv.core @@ -25,6 +25,7 @@ targets: default: filesets : [core] parameters : + - "is_toplevel? (MDU)" - "is_toplevel? (PRE_REGISTER)" - "is_toplevel? (RESET_STRATEGY)" - RISCV_FORMAL @@ -43,6 +44,11 @@ targets: toplevel : serv_rf_top parameters: + MDU: + datatype : int + description: Enables interface for RISC-V standard M-extension + paramtype : vlogparam + PRE_REGISTER: datatype : int description : Register signals before or after the decoder diff --git a/servant.core b/servant.core index 938b2df..1884773 100644 --- a/servant.core +++ b/servant.core @@ -39,7 +39,7 @@ filesets: - "!tool_quartus? (servant/servant_ram.v)" - servant/servant.v file_type : verilogSource - depend : [serv] + depend : [serv, "mdu? (mdu)"] cyc1000: files: @@ -297,7 +297,7 @@ targets: arty_a7_35t: default_tool: vivado filesets : [mem_files, soc, arty_a7_35t] - parameters : [memfile, memsize, frequency=16] + parameters : [memfile, memsize, frequency=16, "mdu? (MDU=1)"] tools: vivado: {part : xc7a35ticsg324-1L} toplevel : servix @@ -347,6 +347,7 @@ targets: filesets : [soc, servant_tb] parameters : - RISCV_FORMAL + - "mdu? (MDU=1)" - SERV_CLEAR_RAM=true - firmware - memsize @@ -379,6 +380,7 @@ targets: filesets : [soc, verilator_tb] parameters : - RISCV_FORMAL + - "mdu? (MDU=1)" - firmware - memsize - signature @@ -429,6 +431,11 @@ parameters: RISCV_FORMAL: datatype : bool paramtype : vlogdefine + + MDU: + datatype : int + description : Enables RISC-V standard M-extension + paramtype : vlogdefine SERV_CLEAR_RAM: datatype : bool diff --git a/servant/servant.v b/servant/servant.v index f300a9a..cae391e 100644 --- a/servant/servant.v +++ b/servant/servant.v @@ -52,6 +52,13 @@ module servant wire wb_timer_cyc; wire [31:0] wb_timer_rdt; + wire [31:0] mdu_rs1; + wire [31:0] mdu_rs2; + wire [ 2:0] mdu_op; + wire mdu_valid; + wire [31:0] mdu_rd; + wire mdu_ready; + servant_arbiter arbiter (.i_wb_cpu_dbus_adr (wb_dmem_adr), .i_wb_cpu_dbus_dat (wb_dmem_dat), @@ -149,6 +156,9 @@ module servant serv_rf_top #(.RESET_PC (32'h0000_0000), .RESET_STRATEGY (reset_strategy), + `ifdef MDU + .MDU(1), + `endif .WITH_CSR (with_csr)) cpu ( @@ -190,6 +200,31 @@ module servant .o_dbus_we (wb_dbus_we), .o_dbus_cyc (wb_dbus_cyc), .i_dbus_rdt (wb_dbus_rdt), - .i_dbus_ack (wb_dbus_ack)); + .i_dbus_ack (wb_dbus_ack), + + //Extension + .o_ext_rs1 (mdu_rs1), + .o_ext_rs2 (mdu_rs2), + .o_ext_funct3 (mdu_op), + .i_ext_rd (mdu_rd), + .i_ext_ready (mdu_ready), + //MDU + .o_mdu_valid (mdu_valid)); + +`ifdef MDU + mdu_top mdu_serv + ( + .i_clk(wb_clk), + .i_rst(wb_rst), + .i_mdu_rs1(mdu_rs1), + .i_mdu_rs2(mdu_rs2), + .i_mdu_op(mdu_op), + .i_mdu_valid(mdu_valid), + .o_mdu_ready(mdu_ready), + .o_mdu_rd(mdu_rd)); +`else + assign mdu_ready = 1'b0; + assign mdu_rd = 32'b0; +`endif endmodule From 621baeff316f8047a97227d5b28441a3a82fd0f8 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Thu, 26 Aug 2021 09:21:51 +0200 Subject: [PATCH 004/157] Always return 0 from reads to reg x0 in serv_rf_ram --- rtl/serv_rf_ram.v | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/rtl/serv_rf_ram.v b/rtl/serv_rf_ram.v index 9bbc503..3cedea6 100644 --- a/rtl/serv_rf_ram.v +++ b/rtl/serv_rf_ram.v @@ -7,16 +7,34 @@ module serv_rf_ram input wire [width-1:0] i_wdata, input wire i_wen, input wire [$clog2(depth)-1:0] i_raddr, - output reg [width-1:0] o_rdata); - + output wire [width-1:0] o_rdata); + reg [width-1:0] memory [0:depth-1]; + reg [width-1:0] rdata ; always @(posedge i_clk) begin if (i_wen) memory[i_waddr] <= i_wdata; - o_rdata <= memory[i_raddr]; + rdata <= memory[i_raddr]; end + /* Reads from reg x0 needs to return 0 + Check that the part of the read address corresponding to the register + is zero and gate the output + width LSB of reg index $clog2(width) + 2 4 1 + 4 3 2 + 8 2 3 + 16 1 4 + 32 0 5 + */ + reg regzero; + + always @(posedge i_clk) + regzero <= !(|i_raddr[$clog2(depth)-1:5-$clog2(width)]); + + assign o_rdata = rdata & ~{width{regzero}}; + `ifdef SERV_CLEAR_RAM integer i; initial From d2a42430335330f6c518a937b7c3a19cdfbffe72 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Thu, 26 Aug 2021 09:24:47 +0200 Subject: [PATCH 005/157] Add reset for new_irq register --- rtl/serv_csr.v | 8 +++++++- rtl/serv_top.v | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/rtl/serv_csr.v b/rtl/serv_csr.v index 49ec01e..d05f962 100644 --- a/rtl/serv_csr.v +++ b/rtl/serv_csr.v @@ -1,7 +1,9 @@ `default_nettype none module serv_csr + #(parameter RESET_STRATEGY = "MINI") ( input wire i_clk, + input wire i_rst, //State input wire i_init, input wire i_en, @@ -76,7 +78,7 @@ module serv_csr timer_irq_r <= timer_irq; o_new_irq <= timer_irq & !timer_irq_r; end - + if (i_mie_en & i_cnt7) mie_mtie <= csr_in; @@ -130,6 +132,10 @@ module serv_csr end if (i_mcause_en & i_cnt_done | i_trap) mcause31 <= i_trap ? o_new_irq : csr_in; + if (i_rst) + if (RESET_STRATEGY != "NONE") + o_new_irq <= 1'b0; + end endmodule diff --git a/rtl/serv_top.v b/rtl/serv_top.v index b517b2e..476e7ec 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -455,9 +455,12 @@ module serv_top generate if (WITH_CSR) begin - serv_csr csr + serv_csr + #(.RESET_STRATEGY (RESET_STRATEGY)) + csr ( .i_clk (clk), + .i_rst (i_rst), //State .i_init (init), .i_en (cnt_en), From b10a87149963090e365e349d8ffa7a7af3fa2d56 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Thu, 26 Aug 2021 10:34:32 +0200 Subject: [PATCH 006/157] Fix signedness bug on immediates The sign bit on immediates relied on the value of csr_imm_en from the previous instruction. This fixes by gating with csr_imm_en after it has been latched instead of before --- rtl/serv_immdec.v | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/rtl/serv_immdec.v b/rtl/serv_immdec.v index aa40e7d..47dce08 100644 --- a/rtl/serv_immdec.v +++ b/rtl/serv_immdec.v @@ -20,7 +20,7 @@ module serv_immdec input wire i_wb_en, input wire [31:7] i_wb_rdt); - reg signbit; + reg imm31; reg [8:0] imm19_12_20; reg imm7; @@ -31,6 +31,8 @@ module serv_immdec assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0]; assign o_csr_imm = imm19_12_20[4]; + wire signbit = imm31 & !i_csr_imm_en; + generate if (SHARED_RFADDR_IMM_REGS) begin assign o_rs1_addr = imm19_12_20[8:4]; @@ -40,7 +42,7 @@ module serv_immdec always @(posedge i_clk) begin if (i_wb_en) begin /* CSR immediates are always zero-extended, hence clear the signbit */ - signbit <= i_wb_rdt[31] & !i_csr_imm_en; + imm31 <= i_wb_rdt[31]; end if (i_wb_en | (i_cnt_en & i_immdec_en[1])) imm19_12_20 <= i_wb_en ? {i_wb_rdt[19:12],i_wb_rdt[20]} : {i_ctrl[3] ? signbit : imm24_20[0], imm19_12_20[8:1]}; @@ -67,7 +69,7 @@ module serv_immdec always @(posedge i_clk) begin if (i_wb_en) begin /* CSR immediates are always zero-extended, hence clear the signbit */ - signbit <= i_wb_rdt[31] & !i_csr_imm_en; + imm31 <= i_wb_rdt[31]; imm19_12_20 <= {i_wb_rdt[19:12],i_wb_rdt[20]}; imm7 <= i_wb_rdt[7]; imm30_25 <= i_wb_rdt[30:25]; From 781c07b7dc8e14880b458286dac0fec52fe05a9a Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Thu, 26 Aug 2021 10:47:03 +0200 Subject: [PATCH 007/157] Properly reset stage_two_req signal --- rtl/serv_state.v | 1 + 1 file changed, 1 insertion(+) diff --git a/rtl/serv_state.v b/rtl/serv_state.v index dfe2ff8..95b347b 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -186,6 +186,7 @@ endgenerate init_done <= 1'b0; o_ctrl_jump <= 1'b0; o_cnt_r <= 4'b0000; + stage_two_req <= 1'b0; end end end From 64f5ca0b7f634dddd77cd49e92adc39d23f75016 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Fri, 27 Aug 2021 12:18:32 +0200 Subject: [PATCH 008/157] Add missing reset on cnt_done --- rtl/serv_state.v | 1 + 1 file changed, 1 insertion(+) diff --git a/rtl/serv_state.v b/rtl/serv_state.v index 95b347b..f47ece7 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -185,6 +185,7 @@ endgenerate o_cnt <= 3'd0; init_done <= 1'b0; o_ctrl_jump <= 1'b0; + o_cnt_done <= 1'b0; o_cnt_r <= 4'b0000; stage_two_req <= 1'b0; end From 3971ca942ed5c9a04384c67994b032006f522b0d Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Fri, 27 Aug 2021 12:18:54 +0200 Subject: [PATCH 009/157] Fix up RVFI --- rtl/serv_top.v | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/rtl/serv_top.v b/rtl/serv_top.v index 476e7ec..1866fb5 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -502,12 +502,18 @@ module serv_top wire rs_en = (branch_op|mem_op|shift_op|slt_op) ? init : ctrl_pc_en; always @(posedge clk) begin + /* End of instruction */ rvfi_valid <= cnt_done & ctrl_pc_en & !i_rst; rvfi_order <= rvfi_order + {63'd0,rvfi_valid}; + + /* Get instruction word when it's fetched from ibus */ if (o_ibus_cyc & i_ibus_ack) rvfi_insn <= i_ibus_rdt; + + /* Store data written to rd */ if (o_wen0) rvfi_rd_wdata <= {o_wdata0,rvfi_rd_wdata[31:1]}; + if (cnt_done & ctrl_pc_en) begin rvfi_pc_rdata <= pc; if (!(rd_en & (|rd_addr))) begin @@ -521,18 +527,22 @@ module serv_top pc <= rvfi_pc_wdata; end + /* Not used */ rvfi_halt <= 1'b0; rvfi_intr <= 1'b0; rvfi_mode <= 2'd3; rvfi_ixl = 2'd1; + + /* RS1 not valid during J, U instructions (immdec_en[1]) */ + /* RS2 not valid during I, J, U instructions (immdec_en[2]) */ if (i_rf_ready) begin - rvfi_rs1_addr <= rs1_addr; - rvfi_rs2_addr <= rs2_addr; + rvfi_rs1_addr <= !immdec_en[1] ? rs1_addr : 5'd0; + rvfi_rs2_addr <= !immdec_en[2] /*rs2_valid*/ ? rs2_addr : 5'd0; rvfi_rd_addr <= rd_addr; end if (rs_en) begin - rvfi_rs1_rdata <= {rs1,rvfi_rs1_rdata[31:1]}; - rvfi_rs2_rdata <= {rs2,rvfi_rs2_rdata[31:1]}; + rvfi_rs1_rdata <= {!immdec_en[1] & rs1,rvfi_rs1_rdata[31:1]}; + rvfi_rs2_rdata <= {!immdec_en[2] & rs2,rvfi_rs2_rdata[31:1]}; end if (i_dbus_ack) begin From 2989051f442eea672ebdb473358288210692640a Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Fri, 27 Aug 2021 13:00:47 +0200 Subject: [PATCH 010/157] Avoid enabling bufreg before instruction is decoded --- rtl/serv_state.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/serv_state.v b/rtl/serv_state.v index f47ece7..cf2a181 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -128,7 +128,7 @@ endgenerate shift : Shift in during phase 1. Continue shifting between phases (except for the first cycle after init). Shift out during phase 2 */ - assign o_bufreg_en = (o_cnt_en & (o_init | o_ctrl_trap | i_branch_op)) | (i_shift_op & !stage_two_req & (i_sh_right | i_sh_done_r)); + assign o_bufreg_en = (o_cnt_en & (o_init | o_ctrl_trap | i_branch_op)) | (i_shift_op & !stage_two_req & (i_sh_right | i_sh_done_r) & init_done); assign o_ibus_cyc = ibus_cyc & !i_rst; From 52d0bf09380d5673308fdc2e423e448b6e2c9abb Mon Sep 17 00:00:00 2001 From: Klas Nordmark Date: Tue, 30 Mar 2021 09:42:00 +0200 Subject: [PATCH 011/157] Added openlane target and params.tcl with suitable openlane parameters for SERV --- data/params.tcl | 6 ++ rtl/serv_synth_wrapper.v | 155 +++++++++++++++++++++++++++++++++++++++ serv.core | 10 +++ 3 files changed, 171 insertions(+) create mode 100644 data/params.tcl create mode 100644 rtl/serv_synth_wrapper.v diff --git a/data/params.tcl b/data/params.tcl new file mode 100644 index 0000000..231da58 --- /dev/null +++ b/data/params.tcl @@ -0,0 +1,6 @@ +set ::env(CLOCK_PERIOD) "10" +set ::env(CLOCK_PORT) "clk" +set ::env(DIE_AREA) "0 0 200 200" +set ::env(FP_SIZING) absolute +set ::env(DESIGN_IS_CORE) 0 +set ::env(GLB_RT_MAXLAYER) 5 diff --git a/rtl/serv_synth_wrapper.v b/rtl/serv_synth_wrapper.v new file mode 100644 index 0000000..5b737d1 --- /dev/null +++ b/rtl/serv_synth_wrapper.v @@ -0,0 +1,155 @@ +`default_nettype none + +module serv_synth_wrapper + #( + /* Register signals before or after the decoder + 0 : Register after the decoder. Faster but uses more resources + 1 : (default) Register before the decoder. Slower but uses less resources + */ + parameter PRE_REGISTER = 1, + /* Amount of reset applied to design + "NONE" : No reset at all. Relies on a POR to set correct initialization + values and that core isn't reset during runtime + "MINI" : Standard setting. Resets the minimal amount of FFs needed to + restart execution from the instruction at RESET_PC + */ + parameter RESET_STRATEGY = "MINI", + parameter WITH_CSR = 1, + parameter RF_WIDTH = 2, + parameter RF_L2D = $clog2((32+(WITH_CSR*4))*32/RF_WIDTH)) + ( + input wire clk, + input wire i_rst, + input wire i_timer_irq, +`ifdef RISCV_FORMAL + output wire rvfi_valid, + output wire [63:0] rvfi_order, + output wire [31:0] rvfi_insn, + output wire rvfi_trap, + output wire rvfi_halt, + output wire rvfi_intr, + output wire [1:0] rvfi_mode, + output wire [1:0] rvfi_ixl, + output wire [4:0] rvfi_rs1_addr, + output wire [4:0] rvfi_rs2_addr, + output wire [31:0] rvfi_rs1_rdata, + output wire [31:0] rvfi_rs2_rdata, + output wire [4:0] rvfi_rd_addr, + output wire [31:0] rvfi_rd_wdata, + output wire [31:0] rvfi_pc_rdata, + output wire [31:0] rvfi_pc_wdata, + output wire [31:0] rvfi_mem_addr, + output wire [3:0] rvfi_mem_rmask, + output wire [3:0] rvfi_mem_wmask, + output wire [31:0] rvfi_mem_rdata, + output wire [31:0] rvfi_mem_wdata, +`endif + output wire [31:0] o_ibus_adr, + output wire o_ibus_cyc, + input wire [31:0] i_ibus_rdt, + input wire i_ibus_ack, + output wire [31:0] o_dbus_adr, + output wire [31:0] o_dbus_dat, + output wire [3:0] o_dbus_sel, + output wire o_dbus_we , + output wire o_dbus_cyc, + input wire [31:0] i_dbus_rdt, + input wire i_dbus_ack, + + output wire [RF_L2D-1:0] o_waddr, + output wire [RF_WIDTH-1:0] o_wdata, + output wire o_wen, + output wire [RF_L2D-1:0] o_raddr, + input wire [RF_WIDTH-1:0] i_rdata); + + localparam CSR_REGS = WITH_CSR*4; + + wire rf_wreq; + wire rf_rreq; + wire [4+WITH_CSR:0] wreg0; + wire [4+WITH_CSR:0] wreg1; + wire wen0; + wire wen1; + wire wdata0; + wire wdata1; + wire [4+WITH_CSR:0] rreg0; + wire [4+WITH_CSR:0] rreg1; + wire rf_ready; + wire rdata0; + wire rdata1; + + serv_rf_ram_if + #(.width (RF_WIDTH), + .reset_strategy (RESET_STRATEGY), + .csr_regs (CSR_REGS)) + rf_ram_if + (.i_clk (clk), + .i_rst (i_rst), + .i_wreq (rf_wreq), + .i_rreq (rf_rreq), + .o_ready (rf_ready), + .i_wreg0 (wreg0), + .i_wreg1 (wreg1), + .i_wen0 (wen0), + .i_wen1 (wen1), + .i_wdata0 (wdata0), + .i_wdata1 (wdata1), + .i_rreg0 (rreg0), + .i_rreg1 (rreg1), + .o_rdata0 (rdata0), + .o_rdata1 (rdata1), + .o_waddr (o_waddr), + .o_wdata (o_wdata), + .o_wen (o_wen), + .o_raddr (o_raddr), + .i_rdata (i_rdata)); + + serv_top + #(.RESET_PC (32'd0), + .PRE_REGISTER (PRE_REGISTER), + .RESET_STRATEGY (RESET_STRATEGY), + .WITH_CSR (WITH_CSR), + .MDU(1'b0)) + cpu + ( + .clk (clk), + .i_rst (i_rst), + .i_timer_irq (i_timer_irq), + .o_rf_rreq (rf_rreq), + .o_rf_wreq (rf_wreq), + .i_rf_ready (rf_ready), + .o_wreg0 (wreg0), + .o_wreg1 (wreg1), + .o_wen0 (wen0), + .o_wen1 (wen1), + .o_wdata0 (wdata0), + .o_wdata1 (wdata1), + .o_rreg0 (rreg0), + .o_rreg1 (rreg1), + .i_rdata0 (rdata0), + .i_rdata1 (rdata1), + + .o_ibus_adr (o_ibus_adr), + .o_ibus_cyc (o_ibus_cyc), + .i_ibus_rdt (i_ibus_rdt), + .i_ibus_ack (i_ibus_ack), + + .o_dbus_adr (o_dbus_adr), + .o_dbus_dat (o_dbus_dat), + .o_dbus_sel (o_dbus_sel), + .o_dbus_we (o_dbus_we), + .o_dbus_cyc (o_dbus_cyc), + .i_dbus_rdt (i_dbus_rdt), + .i_dbus_ack (i_dbus_ack), + + //Extension + .o_ext_funct3 (), + .i_ext_ready (1'b0), + .i_ext_rd (32'd0), + .o_ext_rs1 (), + .o_ext_rs2 (), + //MDU + .o_mdu_valid ()); + +endmodule +`default_nettype wire diff --git a/serv.core b/serv.core index a0cbe14..b11b594 100644 --- a/serv.core +++ b/serv.core @@ -21,6 +21,11 @@ filesets: - rtl/serv_rf_top.v file_type : verilogSource + openlane: + files: + - data/params.tcl : {file_type : tclSource} + - rtl/serv_synth_wrapper.v : {file_type : verilogSource} + targets: default: filesets : [core] @@ -43,6 +48,11 @@ targets: - "-Wall" toplevel : serv_rf_top + sky130: + default_tool : openlane + filesets : [core, openlane] + toplevel : serv_synth_wrapper + parameters: MDU: datatype : int From b15b5ed6525359882d39705ba1ad04cd4bd7be2e Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Wed, 25 Aug 2021 23:44:25 +0200 Subject: [PATCH 012/157] Add Github action to build with openlane+sky130 --- .github/workflows/openlane.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/openlane.yml diff --git a/.github/workflows/openlane.yml b/.github/workflows/openlane.yml new file mode 100644 index 0000000..8fcd4c9 --- /dev/null +++ b/.github/workflows/openlane.yml @@ -0,0 +1,14 @@ +name: build-openlane-sky130 +on: [push] +jobs: + build-openlane: + runs-on: ubuntu-latest + steps: + - name: Checkout subservient + uses: actions/checkout@v2 + - name: Build with Openlane + uses: librecores/ci-fusesoc-action@migrate-dockerized + with: + core: serv + target: sky130 + tool: openlane From b929c31c29ef9bcd292108855702eb87c5b2d42c Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Mon, 27 Sep 2021 22:10:19 +0200 Subject: [PATCH 013/157] Avoid printing directly in do_uart --- bench/servant_tb.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/bench/servant_tb.cpp b/bench/servant_tb.cpp index b8f294a..bf4c4d2 100644 --- a/bench/servant_tb.cpp +++ b/bench/servant_tb.cpp @@ -46,7 +46,7 @@ void uart_init(uart_context_t *context, uint32_t baud_rate) { context->state = 0; } -void do_uart(uart_context_t *context, bool rx) { +bool do_uart(uart_context_t *context, bool rx) { if (context->state == 0) { if (rx) context->state++; @@ -74,10 +74,11 @@ void do_uart(uart_context_t *context, bool rx) { else { if (main_time > context->last_update) { context->last_update += context->baud_t; - putchar(context->ch); context->state=1; + return true; } } + return false; } int main(int argc, char **argv, char **env) @@ -133,11 +134,12 @@ int main(int argc, char **argv, char **env) top->eval(); if (dump) tfp->dump(main_time); - if (baud_rate) - do_uart(&uart_context, top->q); - else + if (baud_rate) { + if (do_uart(&uart_context, top->q)) + putchar(uart_context.ch); + } else { do_gpio(&gpio_context, top->q); - + } if (timeout && (main_time >= timeout)) { printf("Timeout: Exiting at time %lu\n", main_time); done = true; From 48e250ea5e6386ed0a43cb28845ef118b97a75b5 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Sun, 3 Oct 2021 22:48:20 +0200 Subject: [PATCH 014/157] Clean up serv_state interface --- rtl/serv_state.v | 71 ++++++++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/rtl/serv_state.v b/rtl/serv_state.v index cf2a181..03a20d3 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -5,17 +5,31 @@ module serv_state ( input wire i_clk, input wire i_rst, + //State input wire i_new_irq, - input wire i_dbus_ack, - output wire o_ibus_cyc, - input wire i_ibus_ack, - output wire o_rf_rreq, - output wire o_rf_wreq, - input wire i_rf_ready, - output wire o_rf_rd_en, - input wire i_cond_branch, - input wire i_bne_or_bge, input wire i_alu_cmp, + output wire o_init, + output wire o_cnt_en, + output wire o_cnt0to3, + output wire o_cnt12to31, + output wire o_cnt0, + output wire o_cnt1, + output wire o_cnt2, + output wire o_cnt3, + output wire o_cnt7, + output reg o_cnt_done, + output wire o_bufreg_en, + output wire o_ctrl_pc_en, + output reg o_ctrl_jump, + output wire o_ctrl_trap, + input wire i_ctrl_misalign, + input wire i_sh_done, + input wire i_sh_done_r, + output wire [1:0] o_mem_bytecnt, + input wire i_mem_misalign, + //Control + input wire i_bne_or_bge, + input wire i_cond_branch, input wire i_branch_op, input wire i_mem_op, input wire i_shift_op, @@ -23,30 +37,21 @@ module serv_state input wire i_slt_op, input wire i_e_op, input wire i_rd_op, - output wire o_init, - output wire o_cnt_en, - output wire o_cnt0, - output wire o_cnt0to3, - output wire o_cnt12to31, - output wire o_cnt1, - output wire o_cnt2, - output wire o_cnt3, - output wire o_cnt7, - output wire o_ctrl_pc_en, - output reg o_ctrl_jump, - output wire o_ctrl_trap, - input wire i_ctrl_misalign, - input wire i_sh_done, - input wire i_sh_done_r, - output wire o_dbus_cyc, - output wire [1:0] o_mem_bytecnt, - input wire i_mem_misalign, - output reg o_cnt_done, - output wire o_bufreg_en, //MDU - input wire i_mdu_op, - output wire o_mdu_valid, - input wire i_mdu_ready); + input wire i_mdu_op, + output wire o_mdu_valid, + //Extension + input wire i_mdu_ready, + //External + output wire o_dbus_cyc, + input wire i_dbus_ack, + output wire o_ibus_cyc, + input wire i_ibus_ack, + //RF Interface + output wire o_rf_rreq, + output wire o_rf_wreq, + input wire i_rf_ready, + output wire o_rf_rd_en); reg stage_two_req; reg init_done; @@ -97,7 +102,7 @@ generate end else begin //slt*, branch/jump, shift, load/store assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op; - + //valid signal for mdu turned-off assign o_mdu_valid = 1'b0; From 8843005407addaaea872662c5239efba878b1f01 Mon Sep 17 00:00:00 2001 From: Zeeshan Rafique <36025181+zeeshanrafique23@users.noreply.github.com> Date: Mon, 4 Oct 2021 02:15:54 +0500 Subject: [PATCH 015/157] updated vars declaration for modelsim (#63) --- rtl/serv_decode.v | 12 ++++++------ rtl/serv_immdec.v | 3 ++- rtl/serv_top.v | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index f4eb7cf..8a37c8b 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -77,6 +77,12 @@ generate wire co_mem_word; wire co_rd_alu_en; + //opcode + wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); + + wire co_mem_op = !opcode[4] & !opcode[2] & !opcode[0]; + wire co_branch_op = opcode[4] & !opcode[2]; + if (MDU) begin assign co_mdu_op = ((opcode == 5'b01100) & imm25); assign co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; @@ -93,12 +99,6 @@ generate assign co_ext_funct3 = funct3; endgenerate - //opcode - wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); - - wire co_mem_op = !opcode[4] & !opcode[2] & !opcode[0]; - wire co_branch_op = opcode[4] & !opcode[2]; - //jal,branch = imm //jalr = rs1+imm //mem = rs1+imm diff --git a/rtl/serv_immdec.v b/rtl/serv_immdec.v index 47dce08..8be5e45 100644 --- a/rtl/serv_immdec.v +++ b/rtl/serv_immdec.v @@ -28,7 +28,6 @@ module serv_immdec reg [4:0] imm24_20; reg [4:0] imm11_7; - assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0]; assign o_csr_imm = imm19_12_20[4]; wire signbit = imm31 & !i_csr_imm_en; @@ -90,5 +89,7 @@ module serv_immdec end end endgenerate + + assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0]; endmodule diff --git a/rtl/serv_top.v b/rtl/serv_top.v index 1866fb5..20ad315 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -121,6 +121,8 @@ module serv_top wire bufreg_imm_en; wire bufreg_clr_lsb; wire bufreg_q; + wire [31:0] dbus_rdt; + wire dbus_ack; wire alu_sub; wire [1:0] alu_bool_op; @@ -566,8 +568,6 @@ module serv_top `endif generate - wire [31:0] dbus_rdt; - wire dbus_ack; if (MDU) begin assign dbus_rdt = i_ext_ready ? i_ext_rd:i_dbus_rdt; assign dbus_ack = i_dbus_ack | i_ext_ready; From 99f82af6eb6b7fe5dad682bd315349fcd37fa102 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Sun, 3 Oct 2021 23:28:45 +0200 Subject: [PATCH 016/157] Simplify optional MDU logic --- rtl/serv_bufreg.v | 6 +----- rtl/serv_decode.v | 33 +++++++-------------------------- rtl/serv_state.v | 32 ++++++++------------------------ 3 files changed, 16 insertions(+), 55 deletions(-) diff --git a/rtl/serv_bufreg.v b/rtl/serv_bufreg.v index dcf2354..15a194e 100644 --- a/rtl/serv_bufreg.v +++ b/rtl/serv_bufreg.v @@ -46,10 +46,6 @@ module serv_bufreg #( assign o_q = lsb[0] & i_en; assign o_dbus_adr = {data, 2'b00}; assign o_ext_rs1 = {o_dbus_adr[31:2],lsb}; - - generate - if (MDU) assign o_lsb = i_mdu_op ? 2'b00 : lsb; - else assign o_lsb = lsb; - endgenerate + assign o_lsb = (MDU & i_mdu_op) ? 2'b00 : lsb; endmodule diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index 8a37c8b..bc8fed5 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -1,5 +1,5 @@ `default_nettype none -module serv_decode +module serv_decode #(parameter [0:0] PRE_REGISTER = 1, parameter [0:0] MDU = 0) ( @@ -69,35 +69,16 @@ module serv_decode reg imm25; reg imm30; -generate - wire co_mdu_op; - wire [2:0]co_ext_funct3; - wire co_shift_op; - wire co_slt_op; - wire co_mem_word; - wire co_rd_alu_en; - - //opcode wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); + wire co_mdu_op = MDU & (opcode == 5'b01100) & imm25; + wire co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; + wire co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01) & !co_mdu_op; wire co_mem_op = !opcode[4] & !opcode[2] & !opcode[0]; wire co_branch_op = opcode[4] & !opcode[2]; - - if (MDU) begin - assign co_mdu_op = ((opcode == 5'b01100) & imm25); - assign co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; - assign co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01) & !co_mdu_op; - assign co_mem_word = co_mdu_op ? co_mdu_op :funct3[1]; - assign co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4] & !co_mdu_op; - end else begin - assign co_mdu_op = 1'b0; - assign co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01); - assign co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01); - assign co_mem_word = funct3[1]; - assign co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4]; - end - assign co_ext_funct3 = funct3; -endgenerate + wire co_mem_word = co_mdu_op | funct3[1]; + wire co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4] & !co_mdu_op; + wire [2:0] co_ext_funct3 = funct3; //jal,branch = imm //jalr = rs1+imm diff --git a/rtl/serv_state.v b/rtl/serv_state.v index 03a20d3..d036776 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -85,34 +85,18 @@ module serv_state //been calculated. wire take_branch = i_branch_op & (!i_cond_branch | (i_alu_cmp^i_bne_or_bge)); -generate - if (MDU) begin - //slt*, branch/jump, shift, load/store - assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op | i_mdu_op; + //slt*, branch/jump, shift, load/store, (optionally mdu ops) + assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op | (MDU & i_mdu_op); - //valid signal for mdu - assign o_mdu_valid = !o_cnt_en & init_done & i_mdu_op; + //valid signal for mdu + assign o_mdu_valid = MDU & !o_cnt_en & init_done & i_mdu_op; - //Prepare RF for writes when everything is ready to enter stage two - // and the first stage didn't cause a misalign exception - assign o_rf_wreq = !misalign_trap_sync & + //Prepare RF for writes when everything is ready to enter stage two + // and the first stage didn't cause a misalign exception + assign o_rf_wreq = !misalign_trap_sync & ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | - (i_mem_op & i_dbus_ack) | i_mdu_ready | + (i_mem_op & i_dbus_ack) | (MDU & i_mdu_ready) | (stage_two_req & (i_slt_op | i_branch_op))); - end else begin - //slt*, branch/jump, shift, load/store - assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op; - - //valid signal for mdu turned-off - assign o_mdu_valid = 1'b0; - - //Prepare RF for writes when everything is ready to enter stage two - // and the first stage didn't cause a misalign exception - assign o_rf_wreq = !misalign_trap_sync & - ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | - (i_mem_op & i_dbus_ack) | (stage_two_req & (i_slt_op | i_branch_op))); - end -endgenerate assign o_dbus_cyc = !o_cnt_en & init_done & i_mem_op & !i_mem_misalign; From e5c6e788204e19af4d012972027cf9ccd8c036ed Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Mon, 4 Oct 2021 23:38:33 +0200 Subject: [PATCH 017/157] Simplify MDU logic in serv_mem_if --- rtl/serv_decode.v | 2 +- rtl/serv_mem_if.v | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index bc8fed5..c388fd8 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -76,7 +76,7 @@ module serv_decode wire co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01) & !co_mdu_op; wire co_mem_op = !opcode[4] & !opcode[2] & !opcode[0]; wire co_branch_op = opcode[4] & !opcode[2]; - wire co_mem_word = co_mdu_op | funct3[1]; + wire co_mem_word = funct3[1]; wire co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4] & !co_mdu_op; wire [2:0] co_ext_funct3 = funct3; diff --git a/rtl/serv_mem_if.v b/rtl/serv_mem_if.v index ee688ac..261fb8e 100644 --- a/rtl/serv_mem_if.v +++ b/rtl/serv_mem_if.v @@ -61,17 +61,7 @@ module serv_mem_if (i_bytecnt == 2'b00) | (i_half & !i_bytecnt[1]); - wire mem_rd = i_mem_op & (dat_valid ? dat_cur : signbit & i_signed); - - generate - if(MDU) begin - wire mdu_rd = i_mdu_op & dat_cur; - assign o_rd = mem_rd | mdu_rd; - end else begin - wire mdu_rd = 1'b0; - assign o_rd = mem_rd; - end - endgenerate + assign o_rd = (i_mem_op | i_mdu_op) & ((dat_valid|i_mdu_op) ? dat_cur : signbit & i_signed); assign o_wb_sel[3] = (i_lsb == 2'b11) | i_word | (i_half & i_lsb[1]); assign o_wb_sel[2] = (i_lsb == 2'b10) | i_word; From 9d3ebf3e96c52c81f853a35a231220d64c15bf79 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Tue, 5 Oct 2021 09:29:06 +0200 Subject: [PATCH 018/157] Replace mem_op with dedicated control signals --- rtl/serv_decode.v | 23 +++++++++++++++++++---- rtl/serv_mem_if.v | 3 +-- rtl/serv_rf_if.v | 7 ++++--- rtl/serv_state.v | 15 ++++++--------- rtl/serv_top.v | 22 +++++++++++++++------- 5 files changed, 45 insertions(+), 25 deletions(-) diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index c388fd8..4a8da11 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -14,10 +14,11 @@ module serv_decode output reg o_e_op, output reg o_ebreak, output reg o_branch_op, - output reg o_mem_op, output reg o_shift_op, output reg o_slt_op, output reg o_rd_op, + output reg o_two_stage_op, + output reg o_dbus_en, //MDU output reg o_mdu_op, //Extension @@ -52,10 +53,13 @@ module serv_decode output reg [1:0] o_csr_source, output reg o_csr_d_sel, output reg o_csr_imm_en, + output reg o_mtval_pc, //To top output reg [3:0] o_immdec_ctrl, output reg [3:0] o_immdec_en, output reg o_op_b_source, + //To RF IF + output reg o_rd_mem_en, output reg o_rd_csr_en, output reg o_rd_alu_en); @@ -72,12 +76,17 @@ module serv_decode wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); wire co_mdu_op = MDU & (opcode == 5'b01100) & imm25; + wire co_two_stage_op = + ~opcode[2] | (funct3[0] & ~funct3[1] & ~opcode[0] & ~opcode[4]) | + (funct3[1] & ~funct3[2] & ~opcode[0] & ~opcode[4]) | co_mdu_op; wire co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; wire co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01) & !co_mdu_op; - wire co_mem_op = !opcode[4] & !opcode[2] & !opcode[0]; wire co_branch_op = opcode[4] & !opcode[2]; + wire co_dbus_en = ~opcode[2] & ~opcode[4]; + wire co_mtval_pc = opcode[4]; wire co_mem_word = funct3[1]; wire co_rd_alu_en = !opcode[0] & opcode[2] & !opcode[4] & !co_mdu_op; + wire co_rd_mem_en = (!opcode[2] & !opcode[0]) | co_mdu_op; wire [2:0] co_ext_funct3 = funct3; //jal,branch = imm @@ -243,10 +252,12 @@ module serv_decode o_sh_right = co_sh_right; o_bne_or_bge = co_bne_or_bge; o_cond_branch = co_cond_branch; + o_dbus_en = co_dbus_en; + o_mtval_pc = co_mtval_pc; + o_two_stage_op = co_two_stage_op; o_e_op = co_e_op; o_ebreak = co_ebreak; o_branch_op = co_branch_op; - o_mem_op = co_mem_op; o_shift_op = co_shift_op; o_slt_op = co_slt_op; o_rd_op = co_rd_op; @@ -282,6 +293,7 @@ module serv_decode o_op_b_source = co_op_b_source; o_rd_csr_en = co_rd_csr_en; o_rd_alu_en = co_rd_alu_en; + o_rd_mem_en = co_rd_mem_en; end end else begin @@ -304,8 +316,10 @@ module serv_decode o_cond_branch <= co_cond_branch; o_e_op <= co_e_op; o_ebreak <= co_ebreak; + o_two_stage_op <= co_two_stage_op; + o_dbus_en <= co_dbus_en; + o_mtval_pc <= co_mtval_pc; o_branch_op <= co_branch_op; - o_mem_op <= co_mem_op; o_shift_op <= co_shift_op; o_slt_op <= co_slt_op; o_rd_op <= co_rd_op; @@ -341,6 +355,7 @@ module serv_decode o_op_b_source <= co_op_b_source; o_rd_csr_en <= co_rd_csr_en; o_rd_alu_en <= co_rd_alu_en; + o_rd_mem_en <= co_rd_mem_en; end end diff --git a/rtl/serv_mem_if.v b/rtl/serv_mem_if.v index 261fb8e..bbe5715 100644 --- a/rtl/serv_mem_if.v +++ b/rtl/serv_mem_if.v @@ -14,7 +14,6 @@ module serv_mem_if output wire o_sh_done, output wire o_sh_done_r, //Control - input wire i_mem_op, input wire i_shift_op, input wire i_signed, input wire i_word, @@ -61,7 +60,7 @@ module serv_mem_if (i_bytecnt == 2'b00) | (i_half & !i_bytecnt[1]); - assign o_rd = (i_mem_op | i_mdu_op) & ((dat_valid|i_mdu_op) ? dat_cur : signbit & i_signed); + assign o_rd = (dat_valid|i_mdu_op) ? dat_cur : signbit & i_signed; assign o_wb_sel[3] = (i_lsb == 2'b11) | i_word | (i_half & i_lsb[1]); assign o_wb_sel[2] = (i_lsb == 2'b10) | i_word; diff --git a/rtl/serv_rf_if.v b/rtl/serv_rf_if.v index cff08bb..0c5dd67 100644 --- a/rtl/serv_rf_if.v +++ b/rtl/serv_rf_if.v @@ -18,7 +18,7 @@ module serv_rf_if input wire i_trap, input wire i_mret, input wire i_mepc, - input wire i_mem_op, + input wire i_mtval_pc, input wire i_bufreg_q, input wire i_bad_pc, output wire o_csr_pc, @@ -36,6 +36,7 @@ module serv_rf_if input wire i_csr_rd, input wire i_rd_csr_en, input wire i_mem_rd, + input wire i_rd_mem_en, //RS1 read port input wire [4:0] i_rs1_raddr, @@ -56,9 +57,9 @@ module serv_rf_if wire rd = (i_ctrl_rd ) | (i_alu_rd & i_rd_alu_en) | (i_csr_rd & i_rd_csr_en) | - (i_mem_rd); + (i_mem_rd & i_rd_mem_en); - wire mtval = i_mem_op ? i_bufreg_q : i_bad_pc; + wire mtval = i_mtval_pc ? i_bad_pc : i_bufreg_q; assign o_wdata0 = i_trap ? mtval : rd; assign o_wdata1 = i_trap ? i_mepc : i_csr; diff --git a/rtl/serv_state.v b/rtl/serv_state.v index d036776..5dc3ced 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -30,8 +30,9 @@ module serv_state //Control input wire i_bne_or_bge, input wire i_cond_branch, + input wire i_dbus_en, + input wire i_two_stage_op, input wire i_branch_op, - input wire i_mem_op, input wire i_shift_op, input wire i_sh_right, input wire i_slt_op, @@ -56,7 +57,6 @@ module serv_state reg stage_two_req; reg init_done; wire misalign_trap_sync; - wire two_stage_op; reg [4:2] o_cnt; reg [3:0] o_cnt_r; @@ -85,9 +85,6 @@ module serv_state //been calculated. wire take_branch = i_branch_op & (!i_cond_branch | (i_alu_cmp^i_bne_or_bge)); - //slt*, branch/jump, shift, load/store, (optionally mdu ops) - assign two_stage_op = i_slt_op | i_mem_op | i_branch_op | i_shift_op | (MDU & i_mdu_op); - //valid signal for mdu assign o_mdu_valid = MDU & !o_cnt_en & init_done & i_mdu_op; @@ -95,10 +92,10 @@ module serv_state // and the first stage didn't cause a misalign exception assign o_rf_wreq = !misalign_trap_sync & ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | - (i_mem_op & i_dbus_ack) | (MDU & i_mdu_ready) | + i_dbus_ack | (MDU & i_mdu_ready) | (stage_two_req & (i_slt_op | i_branch_op))); - assign o_dbus_cyc = !o_cnt_en & init_done & i_mem_op & !i_mem_misalign; + assign o_dbus_cyc = !o_cnt_en & init_done & i_dbus_en & !i_mem_misalign; //Prepare RF for reads when a new instruction is fetched // or when stage one caused an exception (rreq implies a write request too) @@ -121,7 +118,7 @@ module serv_state assign o_ibus_cyc = ibus_cyc & !i_rst; - assign o_init = two_stage_op & !i_new_irq & !init_done; + assign o_init = i_two_stage_op & !i_new_irq & !init_done; always @(posedge i_clk) begin //ibus_cyc changes on three conditions. @@ -190,7 +187,7 @@ module serv_state //trap_pending is only guaranteed to have correct value during the // last cycle of the init stage wire trap_pending = WITH_CSR & ((take_branch & i_ctrl_misalign) | - (i_mem_op & i_mem_misalign)); + (i_dbus_en & i_mem_misalign)); always @(posedge i_clk) begin if (o_cnt_done) diff --git a/rtl/serv_top.v b/rtl/serv_top.v index 20ad315..e6e9b16 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -78,10 +78,10 @@ module serv_top wire sh_right; wire bne_or_bge; wire cond_branch; + wire two_stage_op; wire e_op; wire ebreak; wire branch_op; - wire mem_op; wire shift_op; wire slt_op; wire rd_op; @@ -89,10 +89,12 @@ module serv_top wire rd_alu_en; wire rd_csr_en; + wire rd_mem_en; wire ctrl_rd; wire alu_rd; wire mem_rd; wire csr_rd; + wire mtval_pc; wire ctrl_pc_en; wire jump; @@ -160,6 +162,7 @@ module serv_top wire csr_imm_en; wire csr_in; wire rf_csr_out; + wire dbus_en; wire new_irq; @@ -200,8 +203,9 @@ module serv_top //Control .i_bne_or_bge (bne_or_bge), .i_cond_branch (cond_branch), + .i_dbus_en (dbus_en), + .i_two_stage_op (two_stage_op), .i_branch_op (branch_op), - .i_mem_op (mem_op), .i_shift_op (shift_op), .i_sh_right (sh_right), .i_slt_op (slt_op), @@ -235,15 +239,16 @@ module serv_top //To state .o_bne_or_bge (bne_or_bge), .o_cond_branch (cond_branch), + .o_dbus_en (dbus_en), .o_e_op (e_op), .o_ebreak (ebreak), .o_branch_op (branch_op), - .o_mem_op (mem_op), .o_shift_op (shift_op), .o_slt_op (slt_op), .o_rd_op (rd_op), .o_sh_right (sh_right), .o_mdu_op (mdu_op), + .o_two_stage_op (two_stage_op), //Extension .o_ext_funct3 (o_ext_funct3), @@ -278,9 +283,12 @@ module serv_top .o_csr_source (csr_source), .o_csr_d_sel (csr_d_sel), .o_csr_imm_en (csr_imm_en), + .o_mtval_pc (mtval_pc ), //To top .o_immdec_ctrl (immdec_ctrl), .o_immdec_en (immdec_en), + //To RF IF + .o_rd_mem_en (rd_mem_en), .o_rd_csr_en (rd_csr_en), .o_rd_alu_en (rd_alu_en)); @@ -396,7 +404,7 @@ module serv_top .i_trap (trap), .i_mret (mret), .i_mepc (o_ibus_adr[0]), - .i_mem_op (mem_op), + .i_mtval_pc (mtval_pc), .i_bufreg_q (bufreg_q), .i_bad_pc (bad_pc), .o_csr_pc (csr_pc), @@ -413,6 +421,7 @@ module serv_top .i_csr_rd (csr_rd), .i_rd_csr_en (rd_csr_en), .i_mem_rd (mem_rd), + .i_rd_mem_en (rd_mem_en), //RS1 read port .i_rs1_raddr (rs1_addr), @@ -441,7 +450,6 @@ module serv_top .o_sh_done_r (mem_sh_done_r), //Control .i_mdu_op (mdu_op), - .i_mem_op (mem_op), .i_shift_op (shift_op), .i_signed (mem_signed), .i_word (mem_word), @@ -470,7 +478,7 @@ module serv_top .i_cnt3 (cnt3), .i_cnt7 (cnt7), .i_cnt_done (cnt_done), - .i_mem_op (mem_op), + .i_mem_op (!mtval_pc), .i_mtip (i_timer_irq), .i_trap (trap), .o_new_irq (new_irq), @@ -501,7 +509,7 @@ module serv_top `ifdef RISCV_FORMAL reg [31:0] pc = RESET_PC; - wire rs_en = (branch_op|mem_op|shift_op|slt_op) ? init : ctrl_pc_en; + wire rs_en = two_stage_op ? init : ctrl_pc_en; always @(posedge clk) begin /* End of instruction */ From 9c4bdd4bfe98e394189bbdd5911c7d008e67a2d5 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Fri, 8 Oct 2021 22:15:55 +0200 Subject: [PATCH 019/157] Simplify branch_op/slt_op signals --- rtl/serv_decode.v | 10 +++++----- rtl/serv_state.v | 8 ++++---- rtl/serv_top.v | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index 4a8da11..7b9a9fd 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -15,7 +15,7 @@ module serv_decode output reg o_ebreak, output reg o_branch_op, output reg o_shift_op, - output reg o_slt_op, + output reg o_slt_or_branch, output reg o_rd_op, output reg o_two_stage_op, output reg o_dbus_en, @@ -80,8 +80,8 @@ module serv_decode ~opcode[2] | (funct3[0] & ~funct3[1] & ~opcode[0] & ~opcode[4]) | (funct3[1] & ~funct3[2] & ~opcode[0] & ~opcode[4]) | co_mdu_op; wire co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; - wire co_slt_op = op_or_opimm & (funct3[2:1] == 2'b01) & !co_mdu_op; - wire co_branch_op = opcode[4] & !opcode[2]; + wire co_slt_or_branch = (opcode[4] | (funct3[1] & opcode[2]) | (imm30 & opcode[2] & opcode[3] & ~funct3[2])) & !co_mdu_op; + wire co_branch_op = opcode[4]; wire co_dbus_en = ~opcode[2] & ~opcode[4]; wire co_mtval_pc = opcode[4]; wire co_mem_word = funct3[1]; @@ -259,7 +259,7 @@ module serv_decode o_ebreak = co_ebreak; o_branch_op = co_branch_op; o_shift_op = co_shift_op; - o_slt_op = co_slt_op; + o_slt_or_branch = co_slt_or_branch; o_rd_op = co_rd_op; o_mdu_op = co_mdu_op; o_ext_funct3 = co_ext_funct3; @@ -321,7 +321,7 @@ module serv_decode o_mtval_pc <= co_mtval_pc; o_branch_op <= co_branch_op; o_shift_op <= co_shift_op; - o_slt_op <= co_slt_op; + o_slt_or_branch <= co_slt_or_branch; o_rd_op <= co_rd_op; o_mdu_op <= co_mdu_op; o_ext_funct3 <= co_ext_funct3; diff --git a/rtl/serv_state.v b/rtl/serv_state.v index 5dc3ced..5f1b336 100644 --- a/rtl/serv_state.v +++ b/rtl/serv_state.v @@ -35,7 +35,7 @@ module serv_state input wire i_branch_op, input wire i_shift_op, input wire i_sh_right, - input wire i_slt_op, + input wire i_slt_or_branch, input wire i_e_op, input wire i_rd_op, //MDU @@ -90,10 +90,10 @@ module serv_state //Prepare RF for writes when everything is ready to enter stage two // and the first stage didn't cause a misalign exception - assign o_rf_wreq = !misalign_trap_sync & - ((i_shift_op & (i_sh_done | !i_sh_right) & !o_cnt_en & init_done) | + assign o_rf_wreq = !misalign_trap_sync & !o_cnt_en & init_done & + ((i_shift_op & (i_sh_done | !i_sh_right)) | i_dbus_ack | (MDU & i_mdu_ready) | - (stage_two_req & (i_slt_op | i_branch_op))); + i_slt_or_branch); assign o_dbus_cyc = !o_cnt_en & init_done & i_dbus_en & !i_mem_misalign; diff --git a/rtl/serv_top.v b/rtl/serv_top.v index e6e9b16..8ed908a 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -83,7 +83,7 @@ module serv_top wire ebreak; wire branch_op; wire shift_op; - wire slt_op; + wire slt_or_branch; wire rd_op; wire mdu_op; @@ -208,7 +208,7 @@ module serv_top .i_branch_op (branch_op), .i_shift_op (shift_op), .i_sh_right (sh_right), - .i_slt_op (slt_op), + .i_slt_or_branch (slt_or_branch), .i_e_op (e_op), .i_rd_op (rd_op), //MDU @@ -244,7 +244,7 @@ module serv_top .o_ebreak (ebreak), .o_branch_op (branch_op), .o_shift_op (shift_op), - .o_slt_op (slt_op), + .o_slt_or_branch (slt_or_branch), .o_rd_op (rd_op), .o_sh_right (sh_right), .o_mdu_op (mdu_op), From 28953fec4c02314f17f78c5666915a43851995fe Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Fri, 8 Oct 2021 22:42:02 +0200 Subject: [PATCH 020/157] Simplify shift_op signal --- rtl/serv_decode.v | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rtl/serv_decode.v b/rtl/serv_decode.v index 7b9a9fd..de6deed 100644 --- a/rtl/serv_decode.v +++ b/rtl/serv_decode.v @@ -73,13 +73,12 @@ module serv_decode reg imm25; reg imm30; - wire op_or_opimm = (!opcode[4] & opcode[2] & !opcode[0]); wire co_mdu_op = MDU & (opcode == 5'b01100) & imm25; wire co_two_stage_op = ~opcode[2] | (funct3[0] & ~funct3[1] & ~opcode[0] & ~opcode[4]) | (funct3[1] & ~funct3[2] & ~opcode[0] & ~opcode[4]) | co_mdu_op; - wire co_shift_op = op_or_opimm & (funct3[1:0] == 2'b01) & !co_mdu_op; + wire co_shift_op = (opcode[2] & ~funct3[1]) & !co_mdu_op; wire co_slt_or_branch = (opcode[4] | (funct3[1] & opcode[2]) | (imm30 & opcode[2] & opcode[3] & ~funct3[2])) & !co_mdu_op; wire co_branch_op = opcode[4]; wire co_dbus_en = ~opcode[2] & ~opcode[4]; From 7765567cf1f717b7eccadca9c85b5e82e5f21616 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Wed, 29 Dec 2021 00:17:00 +0100 Subject: [PATCH 021/157] Add missing gate on mem_rd with CSR disabled --- rtl/serv_rf_if.v | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtl/serv_rf_if.v b/rtl/serv_rf_if.v index 0c5dd67..cf23790 100644 --- a/rtl/serv_rf_if.v +++ b/rtl/serv_rf_if.v @@ -122,7 +122,7 @@ module serv_rf_if end else begin wire rd = (i_ctrl_rd ) | (i_alu_rd & i_rd_alu_en) | - (i_mem_rd); + (i_mem_rd & i_rd_mem_en); assign o_wdata0 = rd; assign o_wdata1 = 1'b0; From 0ab7176d3b155a4390c70146a046fdc0c0cd7b44 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Sat, 1 Jan 2022 16:35:15 +0100 Subject: [PATCH 022/157] Fix testbench indentation --- bench/servant_tb.cpp | 119 +++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/bench/servant_tb.cpp b/bench/servant_tb.cpp index bf4c4d2..1eb6d39 100644 --- a/bench/servant_tb.cpp +++ b/bench/servant_tb.cpp @@ -19,8 +19,8 @@ double sc_time_stamp () { // Called by $time in Verilog void INThandler(int signal) { - printf("\nCaught ctrl-c\n"); - done = true; + printf("\nCaught ctrl-c\n"); + done = true; } typedef struct { @@ -83,73 +83,70 @@ bool do_uart(uart_context_t *context, bool rx) { int main(int argc, char **argv, char **env) { - vluint64_t sample_time = 0; - uint32_t insn = 0; - uint32_t ex_pc = 0; - int baud_rate = 0; + int baud_rate = 0; - gpio_context_t gpio_context; - uart_context_t uart_context; - Verilated::commandArgs(argc, argv); + gpio_context_t gpio_context; + uart_context_t uart_context; + Verilated::commandArgs(argc, argv); - Vservant_sim* top = new Vservant_sim; + Vservant_sim* top = new Vservant_sim; - const char *arg = Verilated::commandArgsPlusMatch("uart_baudrate="); - if (arg[0]) { - baud_rate = atoi(arg+15); - if (baud_rate) { - uart_init(&uart_context, baud_rate); - } - } + const char *arg = Verilated::commandArgsPlusMatch("uart_baudrate="); + if (arg[0]) { + baud_rate = atoi(arg+15); + if (baud_rate) { + uart_init(&uart_context, baud_rate); + } + } - VerilatedVcdC * tfp = 0; - const char *vcd = Verilated::commandArgsPlusMatch("vcd="); - if (vcd[0]) { - Verilated::traceEverOn(true); - tfp = new VerilatedVcdC; - top->trace (tfp, 99); - tfp->open ("trace.vcd"); - } + VerilatedVcdC * tfp = 0; + const char *vcd = Verilated::commandArgsPlusMatch("vcd="); + if (vcd[0]) { + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 99); + tfp->open ("trace.vcd"); + } - signal(SIGINT, INThandler); + signal(SIGINT, INThandler); - vluint64_t timeout = 0; - const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout="); - if (arg_timeout[0]) - timeout = atoi(arg_timeout+9); + vluint64_t timeout = 0; + const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout="); + if (arg_timeout[0]) + timeout = atoi(arg_timeout+9); - vluint64_t vcd_start = 0; - const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start="); - if (arg_vcd_start[0]) - vcd_start = atoi(arg_vcd_start+11); + vluint64_t vcd_start = 0; + const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start="); + if (arg_vcd_start[0]) + vcd_start = atoi(arg_vcd_start+11); - bool dump = false; - top->wb_clk = 1; - bool q = top->q; - while (!(done || Verilated::gotFinish())) { - if (tfp && !dump && (main_time > vcd_start)) { - dump = true; - } - top->wb_rst = main_time < 100; - top->eval(); - if (dump) - tfp->dump(main_time); - if (baud_rate) { - if (do_uart(&uart_context, top->q)) - putchar(uart_context.ch); - } else { - do_gpio(&gpio_context, top->q); - } - if (timeout && (main_time >= timeout)) { - printf("Timeout: Exiting at time %lu\n", main_time); - done = true; - } + bool dump = false; + top->wb_clk = 1; + bool q = top->q; + while (!(done || Verilated::gotFinish())) { + if (tfp && !dump && (main_time > vcd_start)) { + dump = true; + } + top->wb_rst = main_time < 100; + top->eval(); + if (dump) + tfp->dump(main_time); + if (baud_rate) { + if (do_uart(&uart_context, top->q)) + putchar(uart_context.ch); + } else { + do_gpio(&gpio_context, top->q); + } + if (timeout && (main_time >= timeout)) { + printf("Timeout: Exiting at time %lu\n", main_time); + done = true; + } - top->wb_clk = !top->wb_clk; - main_time+=31.25; + top->wb_clk = !top->wb_clk; + main_time+=31.25; - } - if (tfp) - tfp->close(); - exit(0); + } + if (tfp) + tfp->close(); + exit(0); } From 07193819972aed16e8474913dbbbee5e01570e58 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Sat, 1 Jan 2022 17:15:00 +0100 Subject: [PATCH 023/157] Add ViDBo support --- bench/servant_tb_vidbo.cpp | 163 +++++++++++++++++++++++++++++++++++++ servant.core | 4 +- 2 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 bench/servant_tb_vidbo.cpp diff --git a/bench/servant_tb_vidbo.cpp b/bench/servant_tb_vidbo.cpp new file mode 100644 index 0000000..d0a8322 --- /dev/null +++ b/bench/servant_tb_vidbo.cpp @@ -0,0 +1,163 @@ +#include +#include + +#include "verilated_vcd_c.h" +#include "Vservant_sim.h" + +#include "vidbo.h" + +using namespace std; + +static bool done; + +vluint64_t main_time = 0; // Current simulation time +// This is a 64-bit integer to reduce wrap over issues and +// allow modulus. You can also use a double, if you wish. + +double sc_time_stamp () { // Called by $time in Verilog + return main_time; // converts to double, to match + // what SystemC does +} + +void INThandler(int signal) +{ + printf("\nCaught ctrl-c\n"); + done = true; +} + +typedef struct { + bool last_value; +} gpio_context_t; + +void do_gpio(gpio_context_t *context, bool gpio) { + if (context->last_value != gpio) { + context->last_value = gpio; + printf("%lu output q is %s\n", main_time, gpio ? "ON" : "OFF"); + } +} + +typedef struct { + uint8_t state; + char ch; + uint32_t baud_t; + vluint64_t last_update; +} uart_context_t; + +void uart_init(uart_context_t *context, uint32_t baud_rate) { + context->baud_t = 1000*1000*1000/baud_rate; + context->state = 0; +} + +bool do_uart(uart_context_t *context, bool rx) { + if (context->state == 0) { + if (rx) + context->state++; + } + else if (context->state == 1) { + if (!rx) { + context->last_update = main_time + context->baud_t/2; + context->state++; + } + } + else if(context->state == 2) { + if (main_time > context->last_update) { + context->last_update += context->baud_t; + context->ch = 0; + context->state++; + } + } + else if (context->state < 11) { + if (main_time > context->last_update) { + context->last_update += context->baud_t; + context->ch |= rx << (context->state-3); + context->state++; + } + } + else { + if (main_time > context->last_update) { + context->last_update += context->baud_t; + context->state=1; + return true; + } + } + return false; +} + +int main(int argc, char **argv, char **env) +{ + int baud_rate = 0; + + gpio_context_t gpio_context; + uart_context_t uart_context; + + vidbo_context_t vidbo_context; + vidbo_init(&vidbo_context, 8081); + int poll_vidbo = 0; + + Verilated::commandArgs(argc, argv); + + Vservant_sim* top = new Vservant_sim; + + const char *arg = Verilated::commandArgsPlusMatch("uart_baudrate="); + if (arg[0]) { + baud_rate = atoi(arg+15); + if (baud_rate) { + uart_init(&uart_context, baud_rate); + } + } + + VerilatedVcdC * tfp = 0; + const char *vcd = Verilated::commandArgsPlusMatch("vcd="); + if (vcd[0]) { + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 99); + tfp->open ("trace.vcd"); + } + + signal(SIGINT, INThandler); + + vluint64_t timeout = 0; + const char *arg_timeout = Verilated::commandArgsPlusMatch("timeout="); + if (arg_timeout[0]) + timeout = atoi(arg_timeout+9); + + vluint64_t vcd_start = 0; + const char *arg_vcd_start = Verilated::commandArgsPlusMatch("vcd_start="); + if (arg_vcd_start[0]) + vcd_start = atoi(arg_vcd_start+11); + + bool dump = false; + top->wb_clk = 1; + bool q = top->q; + while (!(done || Verilated::gotFinish())) { + if (tfp && !dump && (main_time > vcd_start)) { + dump = true; + } + top->wb_rst = main_time < 100; + top->eval(); + if (dump) + tfp->dump(main_time); + if (baud_rate) { + if (do_uart(&uart_context, top->q)) + vidbo_send(&vidbo_context, main_time, "serial", "uart", uart_context.ch); + } + if (top->q != gpio_context.last_value) { + vidbo_send(&vidbo_context, main_time, "gpio", "LD0", (top->q) & 0x1); + gpio_context.last_value = top->q; + } + if (timeout && (main_time >= timeout)) { + printf("Timeout: Exiting at time %lu\n", main_time); + done = true; + } + + if (!(poll_vidbo++ % 100000)) vidbo_recv(&vidbo_context, 0); + + top->wb_clk = !top->wb_clk; + main_time+=31.25; + + } + if (tfp) + tfp->close(); + exit(0); +} diff --git a/servant.core b/servant.core index 1884773..2df904e 100644 --- a/servant.core +++ b/servant.core @@ -25,8 +25,10 @@ filesets: verilator_tb: files: - bench/servant_sim.v - - bench/servant_tb.cpp : {file_type : cppSource} + - "vidbo? (bench/servant_tb_vidbo.cpp)" : {file_type : cppSource} + - "!vidbo? (bench/servant_tb.cpp)" : {file_type : cppSource} file_type : verilogSource + depend : ["vidbo? (vidbo)"] soc: files: From f04a510393585f0a68280c912ae568b2dccc11cf Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Sat, 1 Jan 2022 22:43:43 +0100 Subject: [PATCH 024/157] Remove unused parameter from serv_mem_if --- rtl/serv_mem_if.v | 3 +-- rtl/serv_top.v | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/rtl/serv_mem_if.v b/rtl/serv_mem_if.v index bbe5715..6c5d260 100644 --- a/rtl/serv_mem_if.v +++ b/rtl/serv_mem_if.v @@ -1,7 +1,6 @@ `default_nettype none module serv_mem_if - #(parameter WITH_CSR = 1, - parameter [0:0] MDU = 0) + #(parameter [0:0] WITH_CSR = 1) ( input wire i_clk, //State diff --git a/rtl/serv_top.v b/rtl/serv_top.v index 8ed908a..73bcbdb 100644 --- a/rtl/serv_top.v +++ b/rtl/serv_top.v @@ -434,8 +434,7 @@ module serv_top .o_csr (rf_csr_out)); serv_mem_if - #(.WITH_CSR (WITH_CSR), - .MDU(MDU)) + #(.WITH_CSR (WITH_CSR)) mem_if ( .i_clk (clk), From d910becd7f2210d575384dc688f2b4b79d16f858 Mon Sep 17 00:00:00 2001 From: Olof Kindgren Date: Sun, 2 Jan 2022 14:08:54 +0100 Subject: [PATCH 025/157] Move dbus_dat/rs2/shamt storage to bufreg2 --- doc/index.rst | 17 +++++--- doc/serv_bufreg2.png | Bin 0 -> 10257 bytes doc/serv_bufreg2_int.png | Bin 0 -> 50431 bytes doc/serv_dataflow.png | Bin 45471 -> 43488 bytes doc/serv_mem_if.png | Bin 10528 -> 8462 bytes doc/serv_mem_if_int.png | Bin 68244 -> 23528 bytes rtl/serv_bufreg2.v | 65 ++++++++++++++++++++++++++++++ rtl/serv_mem_if.v | 84 ++++++++++----------------------------- rtl/serv_top.v | 79 ++++++++++++++++++++++-------------- serv.core | 1 + 10 files changed, 148 insertions(+), 98 deletions(-) create mode 100644 doc/serv_bufreg2.png create mode 100644 doc/serv_bufreg2_int.png create mode 100644 rtl/serv_bufreg2.v diff --git a/doc/index.rst b/doc/index.rst index fe045fd..9cccede 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -53,6 +53,15 @@ For two-stage operations, serv_bufreg holds data between stages. This data can b .. image:: serv_bufreg_int.png +serv_bufreg2 +^^^^^^^^^^^^ + +.. image:: serv_bufreg2.png + +serv_bugreg2 is a 32-bit buffer register with some special features. It is used for shift operations to store the shift amount. It's used in load and store operations to store the data to be written or be read from the data bus, and it holds rs2 for the SERV extension interface. For shift and store operations, the register is shifted in from MSB when dat_en is asserted, while for loads and uses of the extension interface, the whole data word is written to when the i_load signal is asserted. Once the data is in the buffer, it is used differently depending on the operation. For stores and the extension interface the whole buffer is directly connected to the data bus as a 32-bit register. For load operations, the data is fed out serially once it has been fetched from the data bus. To better support load operations of varying sizes the buffer contains logic for reading out data serially from any of the byte LSBs of the 32-bit word. Finally, in shift mode, the 6 LSB of the register is used as a downcounter that is initialized during the init stage and then counts the remaining number of steps to shift the data and signals using sh_done and sh_done_r when finished. + +.. image:: serv_bufreg2_int.png + serv_csr ^^^^^^^^ @@ -101,16 +110,14 @@ serv_mem_if .. image:: serv_mem_if.png -serv_mem_if prepares the data to be sent out on the dbus during store operations and serializes the incoming data during loads +serv_mem_if contains the control logic for preparing the data to be sent out on the dbus during store operations and sign-extends the incoming data from bufreg2 during loads -The memory interface is centered around four byte-wide shift registers connected in series. During store operations, the `dat_en` signal is asserted long enough to shift in the data from rs2 to the right place in the shift registers and the parallel output of the shift registers is then presented to the data bus as a 32-bit word together with a byte mask. The `Data bus byte mask`_ table summarizes the logic for when the individual byte select signals are asserted depending on the two LSB of the data address together with the size (byte, halfword, word) of the write operation. +The memory interface is centered around four serially connected byte-wide shift registers located in serv_bufreg2. During store operations, the `o_byte_valid` signal is asserted long enough to shift in the data from rs2 to the right place in the shift registers. The `Data bus byte mask`_ table summarizes the logic for when the individual byte select signals are asserted depending on the two LSB of the data address together with the size (byte, halfword, word) of the write operation. -During load operations, the data from the bus is latched into the shift registers. `dat_en` is again asserted to shift out data from the registers. `i_lsb` decides from which byte stage of the shift register to tap the data, depending on the alignment of the received data. The `dat_valid` signal makes sure to only present valid data to `o_rd` and otherwise fill in with zeros or sign extension. +During load operations, the data from the bus is read from serv_bufreg2. `dat_en` is again asserted to shift out data from the registers. `i_lsb` decides from which byte stage of the shift register to tap the data, depending on the alignment of the received data. The `dat_valid` signal makes sure to only present valid data to `o_rd` and otherwise fill in with zeros or sign extension. When SERV is built with `WITH_CSR`, there is also logic to detect misaligned accesses which asserts the o_misalign flag to the core. -The shift register used for stores and loads are also used to keep track of the number of steps to shift for left/right shift operations. In this mode, the six LSB of the register is loaded with the shift amount during the init stage and the used as a down counter which raises o_sh_done and o_sh_done_r when the number of shift steps have been completed. - .. image:: serv_mem_if_int.png .. _`Data bus byte mask`: diff --git a/doc/serv_bufreg2.png b/doc/serv_bufreg2.png new file mode 100644 index 0000000000000000000000000000000000000000..c3cd073b3a6175b973f08cc0925b088487cbccce GIT binary patch literal 10257 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 V}WGHG-bjJpdeYJyl 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 literal 0 HcmV?d00001 diff --git a/doc/serv_dataflow.png b/doc/serv_dataflow.png index 6bf1237ee09ca9356c7c5772a2ca86156ea151c9..3e5abe99fdbc0510c62919cbd0d8fb427b1fc700 100644 GIT binary patch literal 43488 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