diff --git a/.gitattributes b/.gitattributes index 7613e20..31ce160 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ fw/project/lcmxo2/*.sty linguist-generated +fw/rtl/serv/* -linguist-vendored fw/rtl/vendor/** -linguist-vendored fw/rtl/vendor/lcmxo2/generated/* linguist-generated hw/pcb/*.html linguist-generated diff --git a/build.sh b/build.sh index 1fde564..fd52d0d 100755 --- a/build.sh +++ b/build.sh @@ -29,6 +29,7 @@ FILES=( BUILT_BOOTLOADER=false BUILT_CONTROLLER=false +BUILT_CIC=false BUILT_FPGA=false BUILT_UPDATE=false BUILT_RELEASE=false @@ -66,6 +67,19 @@ build_controller () { BUILT_CONTROLLER=true } +build_cic () { + if [ "$BUILT_CIC" = true ]; then return; fi + + pushd sw/cic > /dev/null + if [ "$FORCE_CLEAN" = true ]; then + ./build.sh clean + fi + ./build.sh all + popd > /dev/null + + BUILT_CIC=true +} + build_fpga () { if [ "$BUILT_FPGA" = true ]; then return; fi @@ -84,6 +98,7 @@ build_update () { build_bootloader build_controller + build_cic build_fpga pushd sw/tools > /dev/null @@ -126,10 +141,11 @@ build_release () { print_usage () { echo "builder script for SC64" - echo "usage: ./build.sh [bootloader] [controller] [fpga] [update] [release] [-c] [--help]" + echo "usage: ./build.sh [bootloader] [controller] [cic] [fpga] [update] [release] [-c] [--help]" echo "parameters:" echo " bootloader - compile N64 bootloader software" echo " controller - compile MCU controller software" + echo " cic - compile CIC emulation software" echo " fpga - compile FPGA design" echo " update - compile all software and designs" echo " release - collect and zip files for release (triggers 'update' build)" @@ -147,6 +163,7 @@ fi TRIGGER_BOOTLOADER=false TRIGGER_CONTROLLER=false +TRIGGER_CIC=false TRIGGER_FPGA=false TRIGGER_UPDATE=false TRIGGER_RELEASE=false @@ -159,6 +176,9 @@ while test $# -gt 0; do controller) TRIGGER_CONTROLLER=true ;; + cic) + TRIGGER_CIC=true + ;; fpga) TRIGGER_FPGA=true ;; @@ -187,6 +207,7 @@ done if [ "$TRIGGER_BOOTLOADER" = true ]; then build_bootloader; fi if [ "$TRIGGER_CONTROLLER" = true ]; then build_controller; fi +if [ "$TRIGGER_CIC" = true ]; then build_cic; fi if [ "$TRIGGER_FPGA" = true ]; then build_fpga; fi if [ "$TRIGGER_UPDATE" = true ]; then build_update; fi if [ "$TRIGGER_RELEASE" = true ]; then build_release; fi diff --git a/docker_build.sh b/docker_build.sh index 7f580b7..32ae7f9 100755 --- a/docker_build.sh +++ b/docker_build.sh @@ -1,6 +1,6 @@ #!/bin/bash -BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.5" +BUILDER_IMAGE="ghcr.io/polprzewodnikowy/sc64env:v1.6" pushd $(dirname $0) > /dev/null diff --git a/fw/project/lcmxo2/debug.sty b/fw/project/lcmxo2/debug.sty index e2e20d6..821cca4 100644 --- a/fw/project/lcmxo2/debug.sty +++ b/fw/project/lcmxo2/debug.sty @@ -72,6 +72,7 @@ + diff --git a/fw/project/lcmxo2/release.sty b/fw/project/lcmxo2/release.sty index 3408612..c5ef791 100644 --- a/fw/project/lcmxo2/release.sty +++ b/fw/project/lcmxo2/release.sty @@ -72,6 +72,7 @@ + @@ -93,7 +94,7 @@ - + @@ -119,12 +120,12 @@ - + - + @@ -179,8 +180,8 @@ - - + + diff --git a/fw/project/lcmxo2/sc64.ldf b/fw/project/lcmxo2/sc64.ldf index 9b99571..f3198e8 100644 --- a/fw/project/lcmxo2/sc64.ldf +++ b/fw/project/lcmxo2/sc64.ldf @@ -6,6 +6,12 @@ + + + + + + @@ -33,9 +39,15 @@ + + + + + + @@ -48,15 +60,9 @@ - - - - - - @@ -78,9 +84,6 @@ - - - @@ -108,6 +111,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv index e55c36c..9996b23 100644 --- a/fw/rtl/mcu/mcu_top.sv +++ b/fw/rtl/mcu/mcu_top.sv @@ -358,7 +358,9 @@ module mcu_top ( REG_VENDOR_SCR, REG_VENDOR_DATA, REG_DEBUG_0, - REG_DEBUG_1 + REG_DEBUG_1, + REG_CIC_0, + REG_CIC_1 } reg_address_e; logic bootloader_skip; @@ -368,6 +370,8 @@ module mcu_top ( logic dd_bm_ack; + logic cic_invalid_region; + // Register read logic @@ -649,6 +653,22 @@ module mcu_top ( n64_scb.pi_debug[35:32] }; end + + REG_CIC_0: begin + reg_rdata <= { + 4'd0, + cic_invalid_region, + n64_scb.cic_disabled, + n64_scb.cic_64dd_mode, + n64_scb.cic_region, + n64_scb.cic_seed, + n64_scb.cic_checksum[47:32] + }; + end + + REG_CIC_1: begin + reg_rdata <= n64_scb.cic_checksum[31:0]; + end endcase end end @@ -705,6 +725,10 @@ module mcu_top ( dd_bm_ack <= 1'b1; end + if (n64_scb.cic_invalid_region) begin + cic_invalid_region <= 1'b1; + end + if (reset) begin mcu_int <= 1'b0; sd_scb.clock_mode <= 2'd0; @@ -723,6 +747,12 @@ module mcu_top ( flash_scb.erase_pending <= 1'b0; dd_bm_ack <= 1'b0; n64_scb.rtc_wdata_valid <= 1'b0; + cic_invalid_region <= 1'b0; + n64_scb.cic_disabled <= 1'b0; + n64_scb.cic_64dd_mode <= 1'b0; + n64_scb.cic_region <= 1'b0; + n64_scb.cic_seed <= 8'h3F; + n64_scb.cic_checksum <= 48'hA536C0F1D859; end else if (reg_write) begin case (address) REG_MEM_ADDRESS: begin @@ -900,6 +930,21 @@ module mcu_top ( REG_VENDOR_DATA: begin vendor_scb.data_wdata <= reg_wdata; end + + REG_CIC_0: begin + if (reg_wdata[28]) begin + cic_invalid_region <= 1'b0; + end + n64_scb.cic_disabled <= reg_wdata[26]; + n64_scb.cic_64dd_mode <= reg_wdata[25]; + n64_scb.cic_region <= reg_wdata[24]; + n64_scb.cic_seed <= reg_wdata[23:16]; + n64_scb.cic_checksum[47:32] <= reg_wdata[15:0]; + end + + REG_CIC_1: begin + n64_scb.cic_checksum[31:0] <= reg_wdata; + end endcase end end diff --git a/fw/rtl/n64/n64_cic.sv b/fw/rtl/n64/n64_cic.sv new file mode 100644 index 0000000..498f19c --- /dev/null +++ b/fw/rtl/n64/n64_cic.sv @@ -0,0 +1,168 @@ +module n64_cic ( + input clk, + input reset, + + n64_scb.cic n64_scb, + + input n64_reset, + input n64_cic_clk, + inout n64_cic_dq +); + + // Input/output synchronization + + logic [1:0] n64_reset_ff; + logic [1:0] n64_cic_clk_ff; + logic [1:0] n64_cic_dq_ff; + + always_ff @(posedge clk) begin + n64_reset_ff <= {n64_reset_ff[0], n64_reset}; + n64_cic_clk_ff <= {n64_cic_clk_ff[0], n64_cic_clk}; + n64_cic_dq_ff <= {n64_cic_dq_ff[0], n64_cic_dq}; + end + + logic cic_reset; + logic cic_clk; + logic cic_dq; + + always_comb begin + cic_reset = n64_reset_ff[1]; + cic_clk = n64_cic_clk_ff[1]; + cic_dq = n64_cic_dq_ff[1]; + end + + logic cic_dq_out; + + assign n64_cic_dq = cic_dq_out ? 1'bZ : 1'b0; + + + // SERV RISC-V CPU + + logic [31:0] ibus_addr; + logic ibus_cycle; + logic [31:0] ibus_rdata; + logic ibus_ack; + + logic [31:0] dbus_addr; + logic [31:0] dbus_wdata; + logic [3:0] dbus_wmask; + logic dbus_write; + logic dbus_cycle; + logic [31:0] dbus_rdata; + logic dbus_ack; + + logic [31:0] ext_rs1; + logic [31:0] ext_rs2; + logic [2:0] ext_funct3; + logic mdu_valid; + + serv_rf_top #( + .RESET_PC(32'h8000_0000), + .PRE_REGISTER(0), + .WITH_CSR(0) + ) serv_rf_top_inst ( + .clk(clk), + .i_rst(reset), + .i_timer_irq(1'b0), + + .o_ibus_adr(ibus_addr), + .o_ibus_cyc(ibus_cycle), + .i_ibus_rdt(ibus_rdata), + .i_ibus_ack(ibus_ack), + + .o_dbus_adr(dbus_addr), + .o_dbus_dat(dbus_wdata), + .o_dbus_sel(dbus_wmask), + .o_dbus_we(dbus_write) , + .o_dbus_cyc(dbus_cycle), + .i_dbus_rdt(dbus_rdata), + .i_dbus_ack(dbus_ack), + + .o_ext_rs1(ext_rs1), + .o_ext_rs2(ext_rs2), + .o_ext_funct3(ext_funct3), + .i_ext_rd(32'd0), + .i_ext_ready(1'b0), + .o_mdu_valid(mdu_valid) + ); + + + // CPU memory + + logic [8:0] ram_addr; + logic [31:0] ram [0:511]; + logic [31:0] ram_output; + + assign ram_addr = ibus_cycle ? ibus_addr[10:2] : dbus_addr[10:2]; + assign ibus_rdata = ram_output; + + always_ff @(posedge clk) begin + ram_output <= ram[ram_addr]; + + ibus_ack <= ibus_cycle && !ibus_ack; + end + + initial begin + $readmemh("../../../sw/cic/cic.mem", ram); + end + + + // Bus controller + + always_ff @(posedge clk) begin + n64_scb.cic_invalid_region <= 1'b0; + + dbus_ack <= dbus_cycle && !dbus_ack; + + if (dbus_cycle && dbus_write) begin + case (dbus_addr[31:30]) + 2'b10: begin + if (dbus_wmask[0]) ram[ram_addr][7:0] <= dbus_wdata[7:0]; + if (dbus_wmask[1]) ram[ram_addr][15:8] <= dbus_wdata[15:8]; + if (dbus_wmask[2]) ram[ram_addr][23:16] <= dbus_wdata[23:16]; + if (dbus_wmask[3]) ram[ram_addr][31:24] <= dbus_wdata[31:24]; + end + + 2'b11: begin + case (dbus_addr[3:2]) + 2'b10: begin + n64_scb.cic_invalid_region <= dbus_wdata[3]; + cic_dq_out <= dbus_wdata[0]; + end + endcase + end + endcase + end + + if (reset || !cic_reset) begin + cic_dq_out <= 1'b1; + end + end + + always_comb begin + dbus_rdata = 32'd0; + + case (dbus_addr[31:30]) + 2'b10: begin + dbus_rdata = ram_output; + end + + 2'b11: begin + case (dbus_addr[3:2]) + 2'b00: dbus_rdata = { + n64_scb.cic_disabled, + n64_scb.cic_64dd_mode, + n64_scb.cic_region, + n64_scb.cic_seed, + n64_scb.cic_checksum[47:32] + }; + + 2'b01: dbus_rdata = n64_scb.cic_checksum[31:0]; + + 2'b10: dbus_rdata = {29'd0, cic_reset, cic_clk, cic_dq}; + endcase + end + endcase + end + +endmodule diff --git a/fw/rtl/n64/n64_pi.sv b/fw/rtl/n64/n64_pi.sv index 6eed2bf..4e49fa3 100644 --- a/fw/rtl/n64/n64_pi.sv +++ b/fw/rtl/n64/n64_pi.sv @@ -20,16 +20,16 @@ module n64_pi ( logic [1:0] n64_reset_ff; logic [1:0] n64_nmi_ff; - logic [3:0] n64_pi_alel_ff; - logic [3:0] n64_pi_aleh_ff; + logic [2:0] n64_pi_alel_ff; + logic [2:0] n64_pi_aleh_ff; logic [1:0] n64_pi_read_ff; logic [2:0] n64_pi_write_ff; always_ff @(posedge clk) begin n64_reset_ff <= {n64_reset_ff[0], n64_reset}; n64_nmi_ff <= {n64_nmi_ff[0], n64_nmi}; - n64_pi_aleh_ff <= {n64_pi_aleh_ff[2:0], n64_pi_aleh}; - n64_pi_alel_ff <= {n64_pi_alel_ff[2:0], n64_pi_alel}; + n64_pi_aleh_ff <= {n64_pi_aleh_ff[1:0], n64_pi_aleh}; + n64_pi_alel_ff <= {n64_pi_alel_ff[1:0], n64_pi_alel}; n64_pi_read_ff <= {n64_pi_read_ff[0], n64_pi_read}; n64_pi_write_ff <= {n64_pi_write_ff[1:0], n64_pi_write}; end @@ -44,8 +44,8 @@ module n64_pi ( always_comb begin pi_reset = n64_reset_ff[1]; pi_nmi = n64_nmi_ff[1]; - pi_aleh = n64_pi_aleh_ff[3]; - pi_alel = n64_pi_alel_ff[3]; + pi_aleh = n64_pi_aleh_ff[2]; + pi_alel = n64_pi_alel_ff[2]; pi_read = n64_pi_read_ff[1]; pi_write = n64_pi_write_ff[2]; end @@ -98,11 +98,14 @@ module n64_pi ( always_comb begin n64_scb.n64_reset = !last_reset && pi_reset; n64_scb.n64_nmi = !last_nmi && pi_nmi; - aleh_op = pi_reset && (last_pi_mode != PI_MODE_HIGH) && (pi_mode == PI_MODE_HIGH); - alel_op = pi_reset && (last_pi_mode == PI_MODE_HIGH) && (pi_mode == PI_MODE_LOW); - read_op = pi_reset && (pi_mode == PI_MODE_VALID) && (read_port != PORT_NONE) && (last_read && !pi_read); - write_op = pi_reset && (pi_mode == PI_MODE_VALID) && (write_port != PORT_NONE) && (last_write && !pi_write); - end_op = pi_reset && (last_pi_mode == PI_MODE_VALID) && (pi_mode != PI_MODE_VALID); + end + + always_ff @(posedge clk) begin + aleh_op <= pi_reset && (last_pi_mode != PI_MODE_HIGH) && (pi_mode == PI_MODE_HIGH); + alel_op <= pi_reset && (last_pi_mode == PI_MODE_HIGH) && (pi_mode == PI_MODE_LOW); + read_op <= pi_reset && (pi_mode == PI_MODE_VALID) && (read_port != PORT_NONE) && (last_read && !pi_read); + write_op <= pi_reset && (pi_mode == PI_MODE_VALID) && (write_port != PORT_NONE) && (last_write && !pi_write); + end_op <= pi_reset && (last_pi_mode == PI_MODE_VALID) && (pi_mode != PI_MODE_VALID); end diff --git a/fw/rtl/n64/n64_scb.sv b/fw/rtl/n64/n64_scb.sv index 106b70a..b2ba869 100644 --- a/fw/rtl/n64/n64_scb.sv +++ b/fw/rtl/n64/n64_scb.sv @@ -55,6 +55,13 @@ interface n64_scb (); logic [15:0] save_count; + logic cic_invalid_region; + logic cic_disabled; + logic cic_64dd_mode; + logic cic_region; + logic [7:0] cic_seed; + logic [47:0] cic_checksum; + logic pi_sdram_active; logic pi_flash_active; logic [35:0] pi_debug; @@ -98,6 +105,13 @@ interface n64_scb (); input save_count, + input cic_invalid_region, + output cic_disabled, + output cic_64dd_mode, + output cic_region, + output cic_seed, + output cic_checksum, + input pi_debug ); @@ -205,6 +219,15 @@ interface n64_scb (); output save_count ); + modport cic ( + output cic_invalid_region, + input cic_disabled, + input cic_64dd_mode, + input cic_region, + input cic_seed, + input cic_checksum + ); + modport arbiter ( input pi_sdram_active, input pi_flash_active diff --git a/fw/rtl/n64/n64_top.sv b/fw/rtl/n64/n64_top.sv index 2dbc6af..42b4eae 100644 --- a/fw/rtl/n64/n64_top.sv +++ b/fw/rtl/n64/n64_top.sv @@ -18,7 +18,10 @@ module n64_top ( inout [15:0] n64_pi_ad, input n64_si_clk, - inout n64_si_dq + inout n64_si_dq, + + input n64_cic_clk, + inout n64_cic_dq ); logic n64_dd_irq; @@ -101,4 +104,15 @@ module n64_top ( .n64_scb(n64_scb) ); + n64_cic n64_cic_inst ( + .clk(clk), + .reset(reset), + + .n64_scb(n64_scb), + + .n64_reset(n64_reset), + .n64_cic_clk(n64_cic_clk), + .n64_cic_dq(n64_cic_dq) + ); + endmodule diff --git a/fw/rtl/serv/serv_aligner.v b/fw/rtl/serv/serv_aligner.v new file mode 100644 index 0000000..87fceb6 --- /dev/null +++ b/fw/rtl/serv/serv_aligner.v @@ -0,0 +1,67 @@ +module serv_aligner + ( + input wire clk, + input wire rst, + // serv_top + input wire [31:0] i_ibus_adr, + input wire i_ibus_cyc, + output wire [31:0] o_ibus_rdt, + output wire o_ibus_ack, + // serv_rf_top + output wire [31:0] o_wb_ibus_adr, + output wire o_wb_ibus_cyc, + input wire [31:0] i_wb_ibus_rdt, + input wire i_wb_ibus_ack); + + wire [31:0] ibus_rdt_concat; + wire ack_en; + + reg [15:0] lower_hw; + reg ctrl_misal ; + + /* From SERV core to Memory + + o_wb_ibus_adr: Carries address of instruction to memory. In case of misaligned access, + which is caused by pc+2 due to compressed instruction, next instruction is fetched + by pc+4 and concatenation is done to make the instruction aligned. + + o_wb_ibus_cyc: Simply forwarded from SERV to Memory and is only altered by memory or SERV core. + */ + assign o_wb_ibus_adr = ctrl_misal ? (i_ibus_adr+32'b100) : i_ibus_adr; + assign o_wb_ibus_cyc = i_ibus_cyc; + + /* From Memory to SERV core + + o_ibus_ack: Instruction bus acknowledge is send to SERV only when the aligned instruction, + either compressed or un-compressed, is ready to dispatch. + + o_ibus_rdt: Carries the instruction from memory to SERV core. It can be either aligned + instruction coming from memory or made aligned by two bus transactions and concatenation. + */ + assign o_ibus_ack = i_wb_ibus_ack & ack_en; + assign o_ibus_rdt = ctrl_misal ? ibus_rdt_concat : i_wb_ibus_rdt; + + /* 16-bit register used to hold the upper half word of the current instruction in-case + concatenation will be required with the upper half word of upcoming instruction + */ + always @(posedge clk) begin + if(i_wb_ibus_ack)begin + lower_hw <= i_wb_ibus_rdt[31:16]; + end + end + + assign ibus_rdt_concat = {i_wb_ibus_rdt[15:0],lower_hw}; + + /* Two control signals: ack_en, ctrl_misal are set to control the bus transactions between + SERV core and the memory + */ + assign ack_en = !(i_ibus_adr[1] & !ctrl_misal); + + always @(posedge clk ) begin + if(rst) + ctrl_misal <= 0; + else if(i_wb_ibus_ack & i_ibus_adr[1]) + ctrl_misal <= !ctrl_misal; + end + +endmodule diff --git a/fw/rtl/serv/serv_alu.v b/fw/rtl/serv/serv_alu.v new file mode 100644 index 0000000..62755aa --- /dev/null +++ b/fw/rtl/serv/serv_alu.v @@ -0,0 +1,81 @@ +`default_nettype none +module serv_alu + #( + parameter W = 1, + parameter B = W-1 + ) + ( + input wire clk, + //State + input wire i_en, + input wire i_cnt0, + output wire o_cmp, + //Control + input wire i_sub, + input wire [1:0] i_bool_op, + input wire i_cmp_eq, + input wire i_cmp_sig, + input wire [2:0] i_rd_sel, + //Data + input wire [B:0] i_rs1, + input wire [B:0] i_op_b, + input wire [B:0] i_buf, + output wire [B:0] o_rd); + + wire [B:0] result_add; + wire [B:0] result_slt; + + reg cmp_r; + + wire add_cy; + reg [B:0] add_cy_r; + + //Sign-extended operands + wire rs1_sx = i_rs1[B] & i_cmp_sig; + wire op_b_sx = i_op_b[B] & i_cmp_sig; + + wire [B:0] add_b = i_op_b^{W{i_sub}}; + + assign {add_cy,result_add} = i_rs1+add_b+add_cy_r; + + wire result_lt = rs1_sx + ~op_b_sx + add_cy; + + wire result_eq = !(|result_add) & (cmp_r | i_cnt0); + + assign o_cmp = i_cmp_eq ? result_eq : result_lt; + + /* + The result_bool expression implements the following operations between + i_rs1 and i_op_b depending on the value of i_bool_op + + 00 xor + 01 0 + 10 or + 11 and + + i_bool_op will be 01 during shift operations, so by outputting zero under + this condition we can safely or result_bool with i_buf + */ + wire [B:0] result_bool = ((i_rs1 ^ i_op_b) & ~{W{i_bool_op[0]}}) | ({W{i_bool_op[1]}} & i_op_b & i_rs1); + + assign result_slt[0] = cmp_r & i_cnt0; + generate + if (W>1) begin : gen_w_gt_1 + assign result_slt[B:1] = {B{1'b0}}; + end + endgenerate + + assign o_rd = i_buf | + ({W{i_rd_sel[0]}} & result_add) | + ({W{i_rd_sel[1]}} & result_slt) | + ({W{i_rd_sel[2]}} & result_bool); + + always @(posedge clk) begin + add_cy_r <= {W{1'b0}}; + add_cy_r[0] <= i_en ? add_cy : i_sub; + + if (i_en) + cmp_r <= o_cmp; + end + +endmodule diff --git a/fw/rtl/serv/serv_bufreg.v b/fw/rtl/serv/serv_bufreg.v new file mode 100644 index 0000000..15a194e --- /dev/null +++ b/fw/rtl/serv/serv_bufreg.v @@ -0,0 +1,51 @@ +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, + input wire i_mdu_op, + output wire [1:0] o_lsb, + //Control + input wire i_rs1_en, + input wire i_imm_en, + input wire i_clr_lsb, + input wire i_sh_signed, + //Data + input wire i_rs1, + input wire i_imm, + output wire o_q, + //External + 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; + + assign {c,q} = {1'b0,(i_rs1 & i_rs1_en)} + {1'b0,(i_imm & i_imm_en & !clr_lsb)} + c_r; + + always @(posedge i_clk) begin + //Make sure carry is cleared before loading new data + c_r <= c & i_en; + + if (i_en) + data <= {i_init ? q : (data[31] & i_sh_signed), data[31:3]}; + + if (i_init ? (i_cnt0 | i_cnt1) : i_en) + lsb <= {i_init ? q : data[2],lsb[1]}; + end + + assign o_q = lsb[0] & i_en; + assign o_dbus_adr = {data, 2'b00}; + assign o_ext_rs1 = {o_dbus_adr[31:2],lsb}; + assign o_lsb = (MDU & i_mdu_op) ? 2'b00 : lsb; + +endmodule diff --git a/fw/rtl/serv/serv_bufreg2.v b/fw/rtl/serv/serv_bufreg2.v new file mode 100644 index 0000000..57cf753 --- /dev/null +++ b/fw/rtl/serv/serv_bufreg2.v @@ -0,0 +1,65 @@ +module serv_bufreg2 + ( + input wire i_clk, + //State + input wire i_en, + input wire i_init, + input wire i_cnt_done, + input wire [1:0] i_lsb, + input wire i_byte_valid, + output wire o_sh_done, + output wire o_sh_done_r, + //Control + input wire i_op_b_sel, + input wire i_shift_op, + //Data + input wire i_rs2, + input wire i_imm, + output wire o_op_b, + output wire o_q, + //External + output wire [31:0] o_dat, + input wire i_load, + input wire [31:0] i_dat); + + reg [31:0] dat; + + assign o_op_b = i_op_b_sel ? i_rs2 : i_imm; + + wire dat_en = i_shift_op | (i_en & i_byte_valid); + + /* The dat register has three different use cases for store, load and + shift operations. + store : Data to be written is shifted to the correct position in dat during + init by dat_en and is presented on the data bus as o_wb_dat + load : Data from the bus gets latched into dat during i_wb_ack and is then + shifted out at the appropriate time to end up in the correct + position in rd + shift : Data is shifted in during init. After that, the six LSB are used as + a downcounter (with bit 5 initially set to 0) that triggers + o_sh_done and o_sh_done_r when they wrap around to indicate that + the requested number of shifts have been performed + */ + wire [5:0] dat_shamt = (i_shift_op & !i_init) ? + //Down counter mode + dat[5:0]-1 : + //Shift reg mode with optional clearing of bit 5 + {dat[6] & !(i_shift_op & i_cnt_done),dat[5:1]}; + + assign o_sh_done = dat_shamt[5]; + assign o_sh_done_r = dat[5]; + + assign o_q = + ((i_lsb == 2'd3) & dat[24]) | + ((i_lsb == 2'd2) & dat[16]) | + ((i_lsb == 2'd1) & dat[8]) | + ((i_lsb == 2'd0) & dat[0]); + + assign o_dat = dat; + + always @(posedge i_clk) begin + if (dat_en | i_load) + dat <= i_load ? i_dat : {o_op_b, dat[31:7], dat_shamt}; + end + +endmodule diff --git a/fw/rtl/serv/serv_compdec.v b/fw/rtl/serv/serv_compdec.v new file mode 100644 index 0000000..8025249 --- /dev/null +++ b/fw/rtl/serv/serv_compdec.v @@ -0,0 +1,234 @@ +/* Copyright lowRISC contributors. +Copyright 2018 ETH Zurich and University of Bologna, see also CREDITS.md. +Licensed under the Apache License, Version 2.0, see LICENSE for details. +SPDX-License-Identifier: Apache-2.0 + +* Adapted to SERV by @Abdulwadoodd as part of the project under spring '22 LFX Mentorship program */ + +/* Decodes RISC-V compressed instructions into their RV32i equivalent. */ + +module serv_compdec + ( + input wire i_clk, + input wire [31:0] i_instr, + input wire i_ack, + output wire [31:0] o_instr, + output reg o_iscomp); + + localparam OPCODE_LOAD = 7'h03; + localparam OPCODE_OP_IMM = 7'h13; + localparam OPCODE_STORE = 7'h23; + localparam OPCODE_OP = 7'h33; + localparam OPCODE_LUI = 7'h37; + localparam OPCODE_BRANCH = 7'h63; + localparam OPCODE_JALR = 7'h67; + localparam OPCODE_JAL = 7'h6f; + + reg [31:0] comp_instr; + reg illegal_instr; + + assign o_instr = illegal_instr ? i_instr : comp_instr; + + always @(posedge i_clk) begin + if(i_ack) + o_iscomp <= !illegal_instr; + end + + always @ (*) begin + // By default, forward incoming instruction, mark it as legal. + comp_instr = i_instr; + illegal_instr = 1'b0; + + // Check if incoming instruction is compressed. + case (i_instr[1:0]) + // C0 + 2'b00: begin + case (i_instr[15:14]) + 2'b00: begin + // c.addi4spn -> addi rd', x2, imm + comp_instr = {2'b0, i_instr[10:7], i_instr[12:11], i_instr[5], + i_instr[6], 2'b00, 5'h02, 3'b000, 2'b01, i_instr[4:2], {OPCODE_OP_IMM}}; + end + + 2'b01: begin + // c.lw -> lw rd', imm(rs1') + comp_instr = {5'b0, i_instr[5], i_instr[12:10], i_instr[6], + 2'b00, 2'b01, i_instr[9:7], 3'b010, 2'b01, i_instr[4:2], {OPCODE_LOAD}}; + end + + 2'b11: begin + // c.sw -> sw rs2', imm(rs1') + comp_instr = {5'b0, i_instr[5], i_instr[12], 2'b01, i_instr[4:2], + 2'b01, i_instr[9:7], 3'b010, i_instr[11:10], i_instr[6], + 2'b00, {OPCODE_STORE}}; + end + + 2'b10: begin + illegal_instr = 1'b1; + end + + endcase + end + + // C1 + + // Register address checks for RV32E are performed in the regular instruction decoder. + // If this check fails, an illegal instruction exception is triggered and the controller + // writes the actual faulting instruction to mtval. + 2'b01: begin + case (i_instr[15:13]) + 3'b000: begin + // c.addi -> addi rd, rd, nzimm + // c.nop + comp_instr = {{6 {i_instr[12]}}, i_instr[12], i_instr[6:2], + i_instr[11:7], 3'b0, i_instr[11:7], {OPCODE_OP_IMM}}; + end + + 3'b001, 3'b101: begin + // 001: c.jal -> jal x1, imm + // 101: c.j -> jal x0, imm + comp_instr = {i_instr[12], i_instr[8], i_instr[10:9], i_instr[6], + i_instr[7], i_instr[2], i_instr[11], i_instr[5:3], + {9 {i_instr[12]}}, 4'b0, ~i_instr[15], {OPCODE_JAL}}; + end + + 3'b010: begin + // c.li -> addi rd, x0, nzimm + // (c.li hints are translated into an addi hint) + comp_instr = {{6 {i_instr[12]}}, i_instr[12], i_instr[6:2], 5'b0, + 3'b0, i_instr[11:7], {OPCODE_OP_IMM}}; + end + + 3'b011: begin + // c.lui -> lui rd, imm + // (c.lui hints are translated into a lui hint) + comp_instr = {{15 {i_instr[12]}}, i_instr[6:2], i_instr[11:7], {OPCODE_LUI}}; + + if (i_instr[11:7] == 5'h02) begin + // c.addi16sp -> addi x2, x2, nzimm + comp_instr = {{3 {i_instr[12]}}, i_instr[4:3], i_instr[5], i_instr[2], + i_instr[6], 4'b0, 5'h02, 3'b000, 5'h02, {OPCODE_OP_IMM}}; + end + + end + + 3'b100: begin + case (i_instr[11:10]) + 2'b00, + 2'b01: begin + // 00: c.srli -> srli rd, rd, shamt + // 01: c.srai -> srai rd, rd, shamt + // (c.srli/c.srai hints are translated into a srli/srai hint) + comp_instr = {1'b0, i_instr[10], 5'b0, i_instr[6:2], 2'b01, i_instr[9:7], + 3'b101, 2'b01, i_instr[9:7], {OPCODE_OP_IMM}}; + end + + 2'b10: begin + // c.andi -> andi rd, rd, imm + comp_instr = {{6 {i_instr[12]}}, i_instr[12], i_instr[6:2], 2'b01, i_instr[9:7], + 3'b111, 2'b01, i_instr[9:7], {OPCODE_OP_IMM}}; + end + + 2'b11: begin + case (i_instr[6:5]) + 2'b00: begin + // c.sub -> sub rd', rd', rs2' + comp_instr = {2'b01, 5'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], + 3'b000, 2'b01, i_instr[9:7], {OPCODE_OP}}; + end + + 2'b01: begin + // c.xor -> xor rd', rd', rs2' + comp_instr = {7'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], 3'b100, + 2'b01, i_instr[9:7], {OPCODE_OP}}; + end + + 2'b10: begin + // c.or -> or rd', rd', rs2' + comp_instr = {7'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], 3'b110, + 2'b01, i_instr[9:7], {OPCODE_OP}}; + end + + 2'b11: begin + // c.and -> and rd', rd', rs2' + comp_instr = {7'b0, 2'b01, i_instr[4:2], 2'b01, i_instr[9:7], 3'b111, + 2'b01, i_instr[9:7], {OPCODE_OP}}; + end + endcase + end + endcase + end + + 3'b110, 3'b111: begin + // 0: c.beqz -> beq rs1', x0, imm + // 1: c.bnez -> bne rs1', x0, imm + comp_instr = {{4 {i_instr[12]}}, i_instr[6:5], i_instr[2], 5'b0, 2'b01, + i_instr[9:7], 2'b00, i_instr[13], i_instr[11:10], i_instr[4:3], + i_instr[12], {OPCODE_BRANCH}}; + end + endcase + end + + // C2 + + // Register address checks for RV32E are performed in the regular instruction decoder. + // If this check fails, an illegal instruction exception is triggered and the controller + // writes the actual faulting instruction to mtval. + 2'b10: begin + case (i_instr[15:14]) + 2'b00: begin + // c.slli -> slli rd, rd, shamt + // (c.ssli hints are translated into a slli hint) + comp_instr = {7'b0, i_instr[6:2], i_instr[11:7], 3'b001, i_instr[11:7], {OPCODE_OP_IMM}}; + end + + 2'b01: begin + // c.lwsp -> lw rd, imm(x2) + comp_instr = {4'b0, i_instr[3:2], i_instr[12], i_instr[6:4], 2'b00, 5'h02, + 3'b010, i_instr[11:7], OPCODE_LOAD}; + end + + 2'b10: begin + if (i_instr[12] == 1'b0) begin + if (i_instr[6:2] != 5'b0) begin + // c.mv -> add rd/rs1, x0, rs2 + // (c.mv hints are translated into an add hint) + comp_instr = {7'b0, i_instr[6:2], 5'b0, 3'b0, i_instr[11:7], {OPCODE_OP}}; + end else begin + // c.jr -> jalr x0, rd/rs1, 0 + comp_instr = {12'b0, i_instr[11:7], 3'b0, 5'b0, {OPCODE_JALR}}; + end + end else begin + if (i_instr[6:2] != 5'b0) begin + // c.add -> add rd, rd, rs2 + // (c.add hints are translated into an add hint) + comp_instr = {7'b0, i_instr[6:2], i_instr[11:7], 3'b0, i_instr[11:7], {OPCODE_OP}}; + end else begin + if (i_instr[11:7] == 5'b0) begin + // c.ebreak -> ebreak + comp_instr = {32'h00_10_00_73}; + end else begin + // c.jalr -> jalr x1, rs1, 0 + comp_instr = {12'b0, i_instr[11:7], 3'b000, 5'b00001, {OPCODE_JALR}}; + end + end + end + end + + 2'b11: begin + // c.swsp -> sw rs2, imm(x2) + comp_instr = {4'b0, i_instr[8:7], i_instr[12], i_instr[6:2], 5'h02, 3'b010, + i_instr[11:9], 2'b00, {OPCODE_STORE}}; + end + endcase + end + + // Incoming instruction is not compressed. + 2'b11: illegal_instr = 1'b1; + + endcase + end + + endmodule + + diff --git a/fw/rtl/serv/serv_csr.v b/fw/rtl/serv/serv_csr.v new file mode 100644 index 0000000..1bcfc06 --- /dev/null +++ b/fw/rtl/serv/serv_csr.v @@ -0,0 +1,142 @@ +`default_nettype none +module serv_csr + #(parameter RESET_STRATEGY = "MINI") + ( + input wire i_clk, + input wire i_rst, + //State + input wire i_trig_irq, + input wire i_en, + input wire i_cnt0to3, + input wire i_cnt3, + input wire i_cnt7, + input wire i_cnt_done, + input wire i_mem_op, + input wire i_mtip, + input wire i_trap, + output reg o_new_irq, + //Control + input wire i_e_op, + input wire i_ebreak, + input wire i_mem_cmd, + input wire i_mstatus_en, + input wire i_mie_en, + input wire i_mcause_en, + input wire [1:0] i_csr_source, + input wire i_mret, + input wire i_csr_d_sel, + //Data + input wire i_rf_csr_out, + output wire o_csr_in, + input wire i_csr_imm, + input wire i_rs1, + output wire o_q); + + localparam [1:0] + CSR_SOURCE_CSR = 2'b00, + CSR_SOURCE_EXT = 2'b01, + CSR_SOURCE_SET = 2'b10, + CSR_SOURCE_CLR = 2'b11; + + reg mstatus_mie; + reg mstatus_mpie; + reg mie_mtie; + + reg mcause31; + reg [3:0] mcause3_0; + wire mcause; + + wire csr_in; + wire csr_out; + + reg timer_irq_r; + + wire d = i_csr_d_sel ? i_csr_imm : i_rs1; + + assign csr_in = (i_csr_source == CSR_SOURCE_EXT) ? d : + (i_csr_source == CSR_SOURCE_SET) ? csr_out | d : + (i_csr_source == CSR_SOURCE_CLR) ? csr_out & ~d : + (i_csr_source == CSR_SOURCE_CSR) ? csr_out : + 1'bx; + + assign csr_out = (i_mstatus_en & mstatus_mie & i_cnt3) | + i_rf_csr_out | + (i_mcause_en & i_en & mcause); + + assign o_q = csr_out; + + wire timer_irq = i_mtip & mstatus_mie & mie_mtie; + + assign mcause = i_cnt0to3 ? mcause3_0[0] : //[3:0] + i_cnt_done ? mcause31 //[31] + : 1'b0; + + assign o_csr_in = csr_in; + + always @(posedge i_clk) begin + if (i_trig_irq) begin + timer_irq_r <= timer_irq; + o_new_irq <= timer_irq & !timer_irq_r; + end + + if (i_mie_en & i_cnt7) + mie_mtie <= csr_in; + + /* + The mie bit in mstatus gets updated under three conditions + + When a trap is taken, the bit is cleared + During an mret instruction, the bit is restored from mpie + During a mstatus CSR access instruction it's assigned when + bit 3 gets updated + + These conditions are all mutually exclusibe + */ + if ((i_trap & i_cnt_done) | i_mstatus_en & i_cnt3 | i_mret) + mstatus_mie <= !i_trap & (i_mret ? mstatus_mpie : csr_in); + + /* + Note: To save resources mstatus_mpie (mstatus bit 7) is not + readable or writable from sw + */ + if (i_trap & i_cnt_done) + mstatus_mpie <= mstatus_mie; + + /* + The four lowest bits in mcause hold the exception code + + These bits get updated under three conditions + + During an mcause CSR access function, they are assigned when + bits 0 to 3 gets updated + + During an external interrupt the exception code is set to + 7, since SERV only support timer interrupts + + During an exception, the exception code is assigned to indicate + if it was caused by an ebreak instruction (3), + ecall instruction (11), misaligned load (4), misaligned store (6) + or misaligned jump (0) + + The expressions below are derived from the following truth table + irq => 0111 (timer=7) + e_op => x011 (ebreak=3, ecall=11) + mem => 01x0 (store=6, load=4) + ctrl => 0000 (jump=0) + */ + if (i_mcause_en & i_en & i_cnt0to3 | (i_trap & i_cnt_done)) begin + mcause3_0[3] <= (i_e_op & !i_ebreak) | (!i_trap & csr_in); + mcause3_0[2] <= o_new_irq | i_mem_op | (!i_trap & mcause3_0[3]); + mcause3_0[1] <= o_new_irq | i_e_op | (i_mem_op & i_mem_cmd) | (!i_trap & mcause3_0[2]); + mcause3_0[0] <= o_new_irq | i_e_op | (!i_trap & mcause3_0[1]); + 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") begin + o_new_irq <= 1'b0; + mie_mtie <= 1'b0; + end + end + +endmodule diff --git a/fw/rtl/serv/serv_ctrl.v b/fw/rtl/serv/serv_ctrl.v new file mode 100644 index 0000000..ee3e154 --- /dev/null +++ b/fw/rtl/serv/serv_ctrl.v @@ -0,0 +1,84 @@ +`default_nettype none +module serv_ctrl + #(parameter RESET_STRATEGY = "MINI", + parameter RESET_PC = 32'd0, + parameter WITH_CSR = 1) + ( + input wire clk, + input wire i_rst, + //State + input wire i_pc_en, + input wire i_cnt12to31, + input wire i_cnt0, + input wire i_cnt1, + input wire i_cnt2, + //Control + input wire i_jump, + input wire i_jal_or_jalr, + input wire i_utype, + input wire i_pc_rel, + input wire i_trap, + input wire i_iscomp, + //Data + input wire i_imm, + input wire i_buf, + input wire i_csr_pc, + output wire o_rd, + output wire o_bad_pc, + //External + output reg [31:0] o_ibus_adr); + + wire pc_plus_4; + wire pc_plus_4_cy; + reg pc_plus_4_cy_r; + wire pc_plus_offset; + wire pc_plus_offset_cy; + reg pc_plus_offset_cy_r; + wire pc_plus_offset_aligned; + wire plus_4; + + wire pc = o_ibus_adr[0]; + + wire new_pc; + + wire offset_a; + wire offset_b; + + /* If i_iscomp=1: increment pc by 2 else increment pc by 4 */ + + assign plus_4 = i_iscomp ? i_cnt1 : i_cnt2; + + assign o_bad_pc = pc_plus_offset_aligned; + + assign {pc_plus_4_cy,pc_plus_4} = pc+plus_4+pc_plus_4_cy_r; + + generate + if (|WITH_CSR) begin : gen_csr + assign new_pc = i_trap ? (i_csr_pc & !i_cnt0) : i_jump ? pc_plus_offset_aligned : pc_plus_4; + end else begin : gen_no_csr + assign new_pc = i_jump ? pc_plus_offset_aligned : pc_plus_4; + end + endgenerate + assign o_rd = (i_utype & pc_plus_offset_aligned) | (pc_plus_4 & i_jal_or_jalr); + + assign offset_a = i_pc_rel & pc; + assign offset_b = i_utype ? (i_imm & i_cnt12to31): i_buf; + assign {pc_plus_offset_cy,pc_plus_offset} = offset_a+offset_b+pc_plus_offset_cy_r; + + assign pc_plus_offset_aligned = pc_plus_offset & !i_cnt0; + + initial if (RESET_STRATEGY == "NONE") o_ibus_adr = RESET_PC; + + always @(posedge clk) begin + pc_plus_4_cy_r <= i_pc_en & pc_plus_4_cy; + pc_plus_offset_cy_r <= i_pc_en & pc_plus_offset_cy; + + if (RESET_STRATEGY == "NONE") begin + if (i_pc_en) + o_ibus_adr <= {new_pc, o_ibus_adr[31:1]}; + end else begin + if (i_pc_en | i_rst) + o_ibus_adr <= i_rst ? RESET_PC : {new_pc, o_ibus_adr[31:1]}; + end + end +endmodule diff --git a/fw/rtl/serv/serv_decode.v b/fw/rtl/serv/serv_decode.v new file mode 100644 index 0000000..3719819 --- /dev/null +++ b/fw/rtl/serv/serv_decode.v @@ -0,0 +1,365 @@ +`default_nettype none +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, + input wire i_wb_en, + //To state + output reg o_sh_right, + output reg o_bne_or_bge, + output reg o_cond_branch, + output reg o_e_op, + output reg o_ebreak, + output reg o_branch_op, + output reg o_shift_op, + output reg o_slt_or_branch, + output reg o_rd_op, + output reg o_two_stage_op, + output reg o_dbus_en, + //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, + output reg o_bufreg_clr_lsb, + output reg o_bufreg_sh_signed, + //To ctrl + output reg o_ctrl_jal_or_jalr, + output reg o_ctrl_utype, + output reg o_ctrl_pc_rel, + output reg o_ctrl_mret, + //To alu + output reg o_alu_sub, + output reg [1:0] o_alu_bool_op, + output reg o_alu_cmp_eq, + output reg o_alu_cmp_sig, + output reg [2:0] o_alu_rd_sel, + //To mem IF + output reg o_mem_signed, + output reg o_mem_word, + output reg o_mem_half, + output reg o_mem_cmd, + //To CSR + output reg o_csr_en, + output reg [1:0] o_csr_addr, + output reg o_csr_mstatus_en, + output reg o_csr_mie_en, + output reg o_csr_mcause_en, + 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); + + reg [4:0] opcode; + reg [2:0] funct3; + reg op20; + reg op21; + reg op22; + reg op26; + + reg imm25; + reg imm30; + + 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 = (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]; + 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 + //jalr = rs1+imm + //mem = rs1+imm + //shift = rs1 + wire co_bufreg_rs1_en = !opcode[4] | (!opcode[1] & opcode[0]); + wire co_bufreg_imm_en = !opcode[2]; + + //Clear LSB of immediate for BRANCH and JAL ops + //True for BRANCH and JAL + //False for JALR/LOAD/STORE/OP/OPIMM? + wire co_bufreg_clr_lsb = opcode[4] & ((opcode[1:0] == 2'b00) | (opcode[1:0] == 2'b11)); + + //Conditional branch + //True for BRANCH + //False for JAL/JALR + wire co_cond_branch = !opcode[0]; + + wire co_ctrl_utype = !opcode[4] & opcode[2] & opcode[0]; + wire co_ctrl_jal_or_jalr = opcode[4] & opcode[0]; + + //PC-relative operations + //True for jal, b* auipc, ebreak + //False for jalr, lui + wire co_ctrl_pc_rel = (opcode[2:0] == 3'b000) | + (opcode[1:0] == 2'b11) | + (opcode[4] & opcode[2]) & op20| + (opcode[4:3] == 2'b00); + //Write to RD + //True for OP-IMM, AUIPC, OP, LUI, SYSTEM, JALR, JAL, LOAD + //False for STORE, BRANCH, MISC-MEM + wire co_rd_op = (opcode[2] | + (!opcode[2] & opcode[4] & opcode[0]) | + (!opcode[2] & !opcode[3] & !opcode[0])); + + // + //funct3 + // + + wire co_sh_right = funct3[2]; + wire co_bne_or_bge = funct3[0]; + + //Matches system ops except eceall/ebreak/mret + wire csr_op = opcode[4] & opcode[2] & (|funct3); + + + //op20 + wire co_ebreak = op20; + + + //opcode & funct3 & op21 + + wire co_ctrl_mret = opcode[4] & opcode[2] & op21 & !(|funct3); + //Matches system opcodes except CSR accesses (funct3 == 0) + //and mret (!op21) + wire co_e_op = opcode[4] & opcode[2] & !op21 & !(|funct3); + + //opcode & funct3 & imm30 + + wire co_bufreg_sh_signed = imm30; + + /* + True for sub, b*, slt* + False for add* + op opcode f3 i30 + b* 11000 xxx x t + addi 00100 000 x f + slt* 0x100 01x x t + add 01100 000 0 f + sub 01100 000 1 t + */ + wire co_alu_sub = funct3[1] | funct3[0] | (opcode[3] & imm30) | opcode[4]; + + /* + Bits 26, 22, 21 and 20 are enough to uniquely identify the eight supported CSR regs + mtvec, mscratch, mepc and mtval are stored externally (normally in the RF) and are + treated differently from mstatus, mie and mcause which are stored in serv_csr. + + The former get a 2-bit address as seen below while the latter get a + one-hot enable signal each. + + Hex|2 222|Reg |csr + adr|6 210|name |addr + ---|-----|--------|---- + 300|0_000|mstatus | xx + 304|0_100|mie | xx + 305|0_101|mtvec | 01 + 340|1_000|mscratch| 00 + 341|1_001|mepc | 10 + 342|1_010|mcause | xx + 343|1_011|mtval | 11 + + */ + + //true for mtvec,mscratch,mepc and mtval + //false for mstatus, mie, mcause + wire csr_valid = op20 | (op26 & !op21); + + wire co_rd_csr_en = csr_op; + + wire co_csr_en = csr_op & csr_valid; + wire co_csr_mstatus_en = csr_op & !op26 & !op22; + wire co_csr_mie_en = csr_op & !op26 & op22 & !op20; + wire co_csr_mcause_en = csr_op & op21 & !op20; + + wire [1:0] co_csr_source = funct3[1:0]; + wire co_csr_d_sel = funct3[2]; + wire co_csr_imm_en = opcode[4] & opcode[2] & funct3[2]; + wire [1:0] co_csr_addr = {op26 & op20, !op26 | op21}; + + wire co_alu_cmp_eq = funct3[2:1] == 2'b00; + + wire co_alu_cmp_sig = ~((funct3[0] & funct3[1]) | (funct3[1] & funct3[2])); + + wire co_mem_cmd = opcode[3]; + wire co_mem_signed = ~funct3[2]; + wire co_mem_half = funct3[0]; + + wire [1:0] co_alu_bool_op = funct3[1:0]; + + wire [3:0] co_immdec_ctrl; + //True for S (STORE) or B (BRANCH) type instructions + //False for J type instructions + assign co_immdec_ctrl[0] = opcode[3:0] == 4'b1000; + //True for OP-IMM, LOAD, STORE, JALR (I S) + //False for LUI, AUIPC, JAL (U J) + assign co_immdec_ctrl[1] = (opcode[1:0] == 2'b00) | (opcode[2:1] == 2'b00); + assign co_immdec_ctrl[2] = opcode[4] & !opcode[0]; + assign co_immdec_ctrl[3] = opcode[4]; + + wire [3:0] co_immdec_en; + assign co_immdec_en[3] = opcode[4] | opcode[3] | opcode[2] | !opcode[0]; //B I J S U + assign co_immdec_en[2] = (opcode[4] & opcode[2]) | !opcode[3] | opcode[0]; // I J U + assign co_immdec_en[1] = (opcode[2:1] == 2'b01) | (opcode[2] & opcode[0]) | co_csr_imm_en;// J U + assign co_immdec_en[0] = ~co_rd_op; //B S + + wire [2:0] co_alu_rd_sel; + assign co_alu_rd_sel[0] = (funct3 == 3'b000); // Add/sub + assign co_alu_rd_sel[1] = (funct3[2:1] == 2'b01); //SLT* + assign co_alu_rd_sel[2] = funct3[2]; //Bool + + //0 (OP_B_SOURCE_IMM) when OPIMM + //1 (OP_B_SOURCE_RS2) when BRANCH or OP + wire co_op_b_source = opcode[3]; + + generate + if (PRE_REGISTER) begin : gen_pre_register + + always @(posedge clk) begin + 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]; + op22 <= i_wb_rdt[22]; + op26 <= i_wb_rdt[26]; + end + end + + always @(*) begin + 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_shift_op = co_shift_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; + o_bufreg_rs1_en = co_bufreg_rs1_en; + o_bufreg_imm_en = co_bufreg_imm_en; + o_bufreg_clr_lsb = co_bufreg_clr_lsb; + o_bufreg_sh_signed = co_bufreg_sh_signed; + o_ctrl_jal_or_jalr = co_ctrl_jal_or_jalr; + o_ctrl_utype = co_ctrl_utype; + o_ctrl_pc_rel = co_ctrl_pc_rel; + o_ctrl_mret = co_ctrl_mret; + o_alu_sub = co_alu_sub; + o_alu_bool_op = co_alu_bool_op; + o_alu_cmp_eq = co_alu_cmp_eq; + o_alu_cmp_sig = co_alu_cmp_sig; + o_alu_rd_sel = co_alu_rd_sel; + o_mem_signed = co_mem_signed; + o_mem_word = co_mem_word; + o_mem_half = co_mem_half; + o_mem_cmd = co_mem_cmd; + o_csr_en = co_csr_en; + o_csr_addr = co_csr_addr; + o_csr_mstatus_en = co_csr_mstatus_en; + o_csr_mie_en = co_csr_mie_en; + o_csr_mcause_en = co_csr_mcause_en; + o_csr_source = co_csr_source; + o_csr_d_sel = co_csr_d_sel; + o_csr_imm_en = co_csr_imm_en; + o_immdec_ctrl = co_immdec_ctrl; + o_immdec_en = co_immdec_en; + 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 : gen_post_register + + 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]; + op22 = i_wb_rdt[22]; + op26 = i_wb_rdt[26]; + end + + always @(posedge clk) begin + if (i_wb_en) begin + o_sh_right <= co_sh_right; + o_bne_or_bge <= co_bne_or_bge; + 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_shift_op <= co_shift_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; + o_bufreg_rs1_en <= co_bufreg_rs1_en; + o_bufreg_imm_en <= co_bufreg_imm_en; + o_bufreg_clr_lsb <= co_bufreg_clr_lsb; + o_bufreg_sh_signed <= co_bufreg_sh_signed; + o_ctrl_jal_or_jalr <= co_ctrl_jal_or_jalr; + o_ctrl_utype <= co_ctrl_utype; + o_ctrl_pc_rel <= co_ctrl_pc_rel; + o_ctrl_mret <= co_ctrl_mret; + o_alu_sub <= co_alu_sub; + o_alu_bool_op <= co_alu_bool_op; + o_alu_cmp_eq <= co_alu_cmp_eq; + o_alu_cmp_sig <= co_alu_cmp_sig; + o_alu_rd_sel <= co_alu_rd_sel; + o_mem_signed <= co_mem_signed; + o_mem_word <= co_mem_word; + o_mem_half <= co_mem_half; + o_mem_cmd <= co_mem_cmd; + o_csr_en <= co_csr_en; + o_csr_addr <= co_csr_addr; + o_csr_mstatus_en <= co_csr_mstatus_en; + o_csr_mie_en <= co_csr_mie_en; + o_csr_mcause_en <= co_csr_mcause_en; + o_csr_source <= co_csr_source; + o_csr_d_sel <= co_csr_d_sel; + o_csr_imm_en <= co_csr_imm_en; + o_immdec_ctrl <= co_immdec_ctrl; + o_immdec_en <= co_immdec_en; + 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 + + end + endgenerate + +endmodule diff --git a/fw/rtl/serv/serv_immdec.v b/fw/rtl/serv/serv_immdec.v new file mode 100644 index 0000000..1a6e129 --- /dev/null +++ b/fw/rtl/serv/serv_immdec.v @@ -0,0 +1,95 @@ +`default_nettype none +module serv_immdec + #(parameter SHARED_RFADDR_IMM_REGS = 1) + ( + input wire i_clk, + //State + input wire i_cnt_en, + input wire i_cnt_done, + //Control + input wire [3:0] i_immdec_en, + input wire i_csr_imm_en, + input wire [3:0] i_ctrl, + output wire [4:0] o_rd_addr, + output wire [4:0] o_rs1_addr, + output wire [4:0] o_rs2_addr, + //Data + output wire o_csr_imm, + output wire o_imm, + //External + input wire i_wb_en, + input wire [31:7] i_wb_rdt); + + reg imm31; + + reg [8:0] imm19_12_20; + reg imm7; + reg [5:0] imm30_25; + reg [4:0] imm24_20; + reg [4:0] imm11_7; + + assign o_csr_imm = imm19_12_20[4]; + + wire signbit = imm31 & !i_csr_imm_en; + + generate + if (SHARED_RFADDR_IMM_REGS) begin : gen_shared_imm_regs + assign o_rs1_addr = imm19_12_20[8:4]; + assign o_rs2_addr = imm24_20; + assign o_rd_addr = imm11_7; + + always @(posedge i_clk) begin + if (i_wb_en) begin + /* CSR immediates are always zero-extended, hence clear the signbit */ + 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]}; + if (i_wb_en | (i_cnt_en)) + imm7 <= i_wb_en ? i_wb_rdt[7] : signbit; + + if (i_wb_en | (i_cnt_en & i_immdec_en[3])) + imm30_25 <= i_wb_en ? i_wb_rdt[30:25] : {i_ctrl[2] ? imm7 : i_ctrl[1] ? signbit : imm19_12_20[0], imm30_25[5:1]}; + + if (i_wb_en | (i_cnt_en & i_immdec_en[2])) + imm24_20 <= i_wb_en ? i_wb_rdt[24:20] : {imm30_25[0], imm24_20[4:1]}; + + if (i_wb_en | (i_cnt_en & i_immdec_en[0])) + imm11_7 <= i_wb_en ? i_wb_rdt[11:7] : {imm30_25[0], imm11_7[4:1]}; + end + end else begin : gen_separate_imm_regs + reg [4:0] rd_addr; + reg [4:0] rs1_addr; + reg [4:0] rs2_addr; + + assign o_rd_addr = rd_addr; + assign o_rs1_addr = rs1_addr; + assign o_rs2_addr = rs2_addr; + always @(posedge i_clk) begin + if (i_wb_en) begin + /* CSR immediates are always zero-extended, hence clear the signbit */ + 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]; + imm24_20 <= i_wb_rdt[24:20]; + imm11_7 <= i_wb_rdt[11:7]; + + rd_addr <= i_wb_rdt[11:7]; + rs1_addr <= i_wb_rdt[19:15]; + rs2_addr <= i_wb_rdt[24:20]; + end + if (i_cnt_en) begin + imm19_12_20 <= {i_ctrl[3] ? signbit : imm24_20[0], imm19_12_20[8:1]}; + imm7 <= signbit; + imm30_25 <= {i_ctrl[2] ? imm7 : i_ctrl[1] ? signbit : imm19_12_20[0], imm30_25[5:1]}; + imm24_20 <= {imm30_25[0], imm24_20[4:1]}; + imm11_7 <= {imm30_25[0], imm11_7[4:1]}; + end + end + end + endgenerate + + assign o_imm = i_cnt_done ? signbit : i_ctrl[0] ? imm11_7[0] : imm24_20[0]; + +endmodule diff --git a/fw/rtl/serv/serv_mem_if.v b/fw/rtl/serv/serv_mem_if.v new file mode 100644 index 0000000..7cdb39f --- /dev/null +++ b/fw/rtl/serv/serv_mem_if.v @@ -0,0 +1,69 @@ +`default_nettype none +module serv_mem_if + #( + parameter [0:0] WITH_CSR = 1, + parameter W = 1, + parameter B = W-1 + ) + ( + input wire i_clk, + //State + input wire [1:0] i_bytecnt, + input wire [1:0] i_lsb, + output wire o_byte_valid, + output wire o_misalign, + //Control + input wire i_signed, + input wire i_word, + input wire i_half, + //MDU + input wire i_mdu_op, + //Data + input wire [B:0] i_bufreg2_q, + output wire [B:0] o_rd, + //External interface + output wire [3:0] o_wb_sel); + + reg signbit; + + /* + Before a store operation, the data to be written needs to be shifted into + place. Depending on the address alignment, we need to shift different + amounts. One formula for calculating this is to say that we shift when + i_lsb + i_bytecnt < 4. Unfortunately, the synthesis tools don't seem to be + clever enough so the hideous expression below is used to achieve the same + thing in a more optimal way. + */ + assign o_byte_valid + = (!i_lsb[0] & !i_lsb[1]) | + (!i_bytecnt[0] & !i_bytecnt[1]) | + (!i_bytecnt[1] & !i_lsb[1]) | + (!i_bytecnt[1] & !i_lsb[0]) | + (!i_bytecnt[0] & !i_lsb[1]); + + wire dat_valid = + i_mdu_op | + i_word | + (i_bytecnt == 2'b00) | + (i_half & !i_bytecnt[1]); + + assign o_rd = dat_valid ? i_bufreg2_q : {W{i_signed & signbit}}; + + 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; + assign o_wb_sel[1] = (i_lsb == 2'b01) | i_word | (i_half & !i_lsb[1]); + assign o_wb_sel[0] = (i_lsb == 2'b00); + + always @(posedge i_clk) begin + if (dat_valid) + signbit <= i_bufreg2_q[B]; + end + + /* + mem_misalign is checked after the init stage to decide whether to do a data + bus transaction or go to the trap state. It is only guaranteed to be correct + at this time + */ + assign o_misalign = WITH_CSR & ((i_lsb[0] & (i_word | i_half)) | (i_lsb[1] & i_word)); + +endmodule diff --git a/fw/rtl/serv/serv_rf_if.v b/fw/rtl/serv/serv_rf_if.v new file mode 100644 index 0000000..81d4c85 --- /dev/null +++ b/fw/rtl/serv/serv_rf_if.v @@ -0,0 +1,149 @@ +`default_nettype none +module serv_rf_if + #(parameter WITH_CSR = 1) + (//RF Interface + input wire i_cnt_en, + output wire [4+WITH_CSR:0] o_wreg0, + output wire [4+WITH_CSR:0] o_wreg1, + output wire o_wen0, + output wire o_wen1, + output wire o_wdata0, + output wire o_wdata1, + output wire [4+WITH_CSR:0] o_rreg0, + output wire [4+WITH_CSR:0] o_rreg1, + input wire i_rdata0, + input wire i_rdata1, + + //Trap interface + input wire i_trap, + input wire i_mret, + input wire i_mepc, + input wire i_mtval_pc, + input wire i_bufreg_q, + input wire i_bad_pc, + output wire o_csr_pc, + //CSR interface + input wire i_csr_en, + input wire [1:0] i_csr_addr, + input wire i_csr, + output wire o_csr, + //RD write port + input wire i_rd_wen, + input wire [4:0] i_rd_waddr, + input wire i_ctrl_rd, + input wire i_alu_rd, + input wire i_rd_alu_en, + 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, + output wire o_rs1, + //RS2 read port + input wire [4:0] i_rs2_raddr, + output wire o_rs2); + + + /* + ********** Write side *********** + */ + + wire rd_wen = i_rd_wen & (|i_rd_waddr); + + generate + if (|WITH_CSR) begin : gen_csr + wire rd = (i_ctrl_rd ) | + (i_alu_rd & i_rd_alu_en) | + (i_csr_rd & i_rd_csr_en) | + (i_mem_rd & i_rd_mem_en); + + 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; + + /* Port 0 handles writes to mtval during traps and rd otherwise + * Port 1 handles writes to mepc during traps and csr accesses otherwise + * + * GPR registers are mapped to address 0-31 (bits 0xxxxx). + * Following that are four CSR registers + * mscratch 100000 + * mtvec 100001 + * mepc 100010 + * mtval 100011 + */ + + assign o_wreg0 = i_trap ? {6'b100011} : {1'b0,i_rd_waddr}; + assign o_wreg1 = i_trap ? {6'b100010} : {4'b1000,i_csr_addr}; + + assign o_wen0 = i_cnt_en & (i_trap | rd_wen); + assign o_wen1 = i_cnt_en & (i_trap | i_csr_en); + + /* + ********** Read side *********** + */ + + //0 : RS1 + //1 : RS2 / CSR + + assign o_rreg0 = {1'b0, i_rs1_raddr}; + + /* + The address of the second read port (o_rreg1) can get assigned from four + different sources + + Normal operations : i_rs2_raddr + CSR access : i_csr_addr + trap : MTVEC + mret : MEPC + + Address 0-31 in the RF are assigned to the GPRs. After that follows the four + CSRs on addresses 32-35 + + 32 MSCRATCH + 33 MTVEC + 34 MEPC + 35 MTVAL + + The expression below is an optimized version of this logic + */ + wire sel_rs2 = !(i_trap | i_mret | i_csr_en); + assign o_rreg1 = {~sel_rs2, + i_rs2_raddr[4:2] & {3{sel_rs2}}, + {1'b0,i_trap} | {i_mret,1'b0} | ({2{i_csr_en}} & i_csr_addr) | ({2{sel_rs2}} & i_rs2_raddr[1:0])}; + + assign o_rs1 = i_rdata0; + assign o_rs2 = i_rdata1; + assign o_csr = i_rdata1 & i_csr_en; + assign o_csr_pc = i_rdata1; + + end else begin : gen_no_csr + wire rd = (i_ctrl_rd ) | + (i_alu_rd & i_rd_alu_en) | + (i_mem_rd & i_rd_mem_en); + + assign o_wdata0 = rd; + assign o_wdata1 = 1'b0; + + assign o_wreg0 = i_rd_waddr; + assign o_wreg1 = 5'd0; + + assign o_wen0 = i_cnt_en & rd_wen; + assign o_wen1 = 1'b0; + + /* + ********** Read side *********** + */ + + assign o_rreg0 = i_rs1_raddr; + assign o_rreg1 = i_rs2_raddr; + + assign o_rs1 = i_rdata0; + assign o_rs2 = i_rdata1; + assign o_csr = 1'b0; + assign o_csr_pc = 1'b0; + end // else: !if(WITH_CSR) + endgenerate +endmodule diff --git a/fw/rtl/serv/serv_rf_ram.v b/fw/rtl/serv/serv_rf_ram.v new file mode 100644 index 0000000..6c96fa6 --- /dev/null +++ b/fw/rtl/serv/serv_rf_ram.v @@ -0,0 +1,45 @@ +module serv_rf_ram + #(parameter width=0, + parameter csr_regs=4, + parameter depth=32*(32+csr_regs)/width) + (input wire i_clk, + input wire [$clog2(depth)-1:0] i_waddr, + input wire [width-1:0] i_wdata, + input wire i_wen, + input wire [$clog2(depth)-1:0] i_raddr, + input wire i_ren, + 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; + rdata <= i_ren ? memory[i_raddr] : {width{1'bx}}; + 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 + for (i=0;i2) begin : gen_rdata1_w_neq_2 + always @(posedge i_clk) begin + rdata1 <= {1'b0,rdata1[width-2:1]}; //Optimize? + if (rtrig1) + rdata1[width-2:0] <= i_rdata[width-1:1]; + end + end else begin : gen_rdata1_w_eq_2 + always @(posedge i_clk) if (rtrig1) rdata1 <= i_rdata[1]; + end + endgenerate + + always @(posedge i_clk) begin + if (&rcnt | i_rreq) + rgate <= i_rreq; + + rtrig1 <= rtrig0; + rcnt <= rcnt+5'd1; + if (i_rreq | i_wreq) + rcnt <= {3'd0,i_wreq,1'b0}; + + rreq_r <= i_rreq; + rgnt <= rreq_r; + + rdata0 <= {1'b0,rdata0[width-1:1]}; + if (rtrig0) + rdata0 <= i_rdata; + + if (i_rst) begin + if (reset_strategy != "NONE") begin + rgate <= 1'b0; + rgnt <= 1'b0; + rreq_r <= 1'b0; + rcnt <= 5'd0; + end + end + end + + + +endmodule diff --git a/fw/rtl/serv/serv_rf_top.v b/fw/rtl/serv/serv_rf_top.v new file mode 100644 index 0000000..35b7662 --- /dev/null +++ b/fw/rtl/serv/serv_rf_top.v @@ -0,0 +1,216 @@ +`default_nettype none + +module serv_rf_top + #(parameter RESET_PC = 32'd0, + /* COMPRESSED=1: Enable the compressed decoder and allowed misaligned jump of pc + COMPRESSED=0: Disable the compressed decoder and does not allow the misaligned jump of pc + */ + parameter [0:0] COMPRESSED = 0, + /* + ALIGN = 1: Fetch the aligned instruction by making two bus transactions if the misaligned address + is given to the instruction bus. + */ + parameter [0:0] ALIGN = COMPRESSED, + /* 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 + */ + 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, + + // 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; + 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; + + wire [RF_L2D-1:0] waddr; + wire [RF_WIDTH-1:0] wdata; + wire wen; + wire [RF_L2D-1:0] raddr; + wire ren; + wire [RF_WIDTH-1:0] rdata; + + 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 (waddr), + .o_wdata (wdata), + .o_wen (wen), + .o_raddr (raddr), + .o_ren (ren), + .i_rdata (rdata)); + + serv_rf_ram + #(.width (RF_WIDTH), + .csr_regs (CSR_REGS)) + rf_ram + (.i_clk (clk), + .i_waddr (waddr), + .i_wdata (wdata), + .i_wen (wen), + .i_raddr (raddr), + .i_ren (ren), + .o_rdata (rdata)); + + serv_top + #(.RESET_PC (RESET_PC), + .PRE_REGISTER (PRE_REGISTER), + .RESET_STRATEGY (RESET_STRATEGY), + .WITH_CSR (WITH_CSR), + .MDU(MDU), + .COMPRESSED(COMPRESSED), + .ALIGN(ALIGN)) + cpu + ( + .clk (clk), + .i_rst (i_rst), + .i_timer_irq (i_timer_irq), +`ifdef RISCV_FORMAL + .rvfi_valid (rvfi_valid ), + .rvfi_order (rvfi_order ), + .rvfi_insn (rvfi_insn ), + .rvfi_trap (rvfi_trap ), + .rvfi_halt (rvfi_halt ), + .rvfi_intr (rvfi_intr ), + .rvfi_mode (rvfi_mode ), + .rvfi_ixl (rvfi_ixl ), + .rvfi_rs1_addr (rvfi_rs1_addr ), + .rvfi_rs2_addr (rvfi_rs2_addr ), + .rvfi_rs1_rdata (rvfi_rs1_rdata), + .rvfi_rs2_rdata (rvfi_rs2_rdata), + .rvfi_rd_addr (rvfi_rd_addr ), + .rvfi_rd_wdata (rvfi_rd_wdata ), + .rvfi_pc_rdata (rvfi_pc_rdata ), + .rvfi_pc_wdata (rvfi_pc_wdata ), + .rvfi_mem_addr (rvfi_mem_addr ), + .rvfi_mem_rmask (rvfi_mem_rmask), + .rvfi_mem_wmask (rvfi_mem_wmask), + .rvfi_mem_rdata (rvfi_mem_rdata), + .rvfi_mem_wdata (rvfi_mem_wdata), +`endif + .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 (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/fw/rtl/serv/serv_state.v b/fw/rtl/serv/serv_state.v new file mode 100644 index 0000000..012a85e --- /dev/null +++ b/fw/rtl/serv/serv_state.v @@ -0,0 +1,224 @@ +module serv_state + #(parameter RESET_STRATEGY = "MINI", + parameter [0:0] WITH_CSR = 1, + parameter [0:0] ALIGN =0, + parameter [0:0] MDU = 0, + parameter W = 1 + ) + ( + input wire i_clk, + input wire i_rst, + //State + input wire i_new_irq, + input wire i_alu_cmp, + output wire o_init, + output reg 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 wire 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_dbus_en, + input wire i_two_stage_op, + input wire i_branch_op, + input wire i_shift_op, + input wire i_sh_right, + input wire i_slt_or_branch, + input wire i_e_op, + input wire i_rd_op, + //MDU + 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; + wire misalign_trap_sync; + + reg [4:2] o_cnt; + reg [3:0] cnt_r; + + reg ibus_cyc; + //Update PC in RUN or TRAP states + assign o_ctrl_pc_en = o_cnt_en & !o_init; + + assign o_mem_bytecnt = o_cnt[4:3]; + + assign o_cnt0to3 = (o_cnt[4:2] == 3'd0); + assign o_cnt12to31 = (o_cnt[4] | (o_cnt[3:2] == 2'b11)); + assign o_cnt0 = (o_cnt[4:2] == 3'd0) & cnt_r[0]; + assign o_cnt1 = (o_cnt[4:2] == 3'd0) & cnt_r[1]; + assign o_cnt2 = (o_cnt[4:2] == 3'd0) & cnt_r[2]; + assign o_cnt3 = (o_cnt[4:2] == 3'd0) & cnt_r[3]; + assign o_cnt7 = (o_cnt[4:2] == 3'd1) & cnt_r[3]; + + //Take branch for jump or branch instructions (opcode == 1x0xx) if + //a) It's an unconditional branch (opcode[0] == 1) + //b) It's a conditional branch (opcode[0] == 0) of type beq,blt,bltu (funct3[0] == 0) and ALU compare is true + //c) It's a conditional branch (opcode[0] == 0) of type bne,bge,bgeu (funct3[0] == 1) and ALU compare is false + //Only valid during the last cycle of INIT, when the branch condition has + //been calculated. + wire take_branch = i_branch_op & (!i_cond_branch | (i_alu_cmp^i_bne_or_bge)); + + //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 & !o_cnt_en & init_done & + ((i_shift_op & (i_sh_done | !i_sh_right)) | + i_dbus_ack | (MDU & i_mdu_ready) | + i_slt_or_branch); + + 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) + assign o_rf_rreq = i_ibus_ack | (stage_two_req & misalign_trap_sync); + + assign o_rf_rd_en = i_rd_op & !o_init; + + /* + bufreg is used during mem. branch and shift operations + + mem : bufreg is used for dbus address. Shift in data during phase 1. + Shift out during phase 2 if there was an misalignment exception. + + branch : Shift in during phase 1. Shift out during phase 2 + + 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_two_stage_op))) | (i_shift_op & !stage_two_req & (i_sh_right | i_sh_done_r) & init_done); + + assign o_ibus_cyc = ibus_cyc & !i_rst; + + assign o_init = i_two_stage_op & !i_new_irq & !init_done; + + assign o_cnt_done = (o_cnt[4:2] == 3'b111) & cnt_r[3]; + + always @(posedge i_clk) begin + //ibus_cyc changes on three conditions. + //1. i_rst is asserted. Together with the async gating above, o_ibus_cyc + // will be asserted as soon as the reset is released. This is how the + // first instruction is fetced + //2. o_cnt_done and o_ctrl_pc_en are asserted. This means that SERV just + // finished updating the PC, is done with the current instruction and + // o_ibus_cyc gets asserted to fetch a new instruction + //3. When i_ibus_ack, a new instruction is fetched and o_ibus_cyc gets + // deasserted to finish the transaction + if (i_ibus_ack | o_cnt_done | i_rst) + ibus_cyc <= o_ctrl_pc_en | i_rst; + + if (o_cnt_done) begin + init_done <= o_init & !init_done; + o_ctrl_jump <= o_init & take_branch; + end + + //Need a strobe for the first cycle in the IDLE state after INIT + stage_two_req <= o_cnt_done & o_init; + + if (i_rst) begin + if (RESET_STRATEGY != "NONE") begin + init_done <= 1'b0; + o_ctrl_jump <= 1'b0; + stage_two_req <= 1'b0; + end + end + end + + always @(posedge i_clk) begin + /* + Because SERV is 32-bit bit-serial we need a counter than can count 0-31 + to keep track of which bit we are currently processing. o_cnt and cnt_r + are used together to create such a counter. + The top three bits (o_cnt) are implemented as a normal counter, but + instead of the two LSB, cnt_r is a 4-bit shift register which loops 0-3 + When cnt_r[3] is 1, o_cnt will be increased. + + The counting starts when the core is idle and the i_rf_ready signal + comes in from the RF module by shifting in the i_rf_ready bit as LSB of + the shift register. Counting is stopped by using o_cnt_done to block the + bit that was supposed to be shifted into bit 0 of cnt_r. + + There are two benefit of doing the counter this way + 1. We only need to check four bits instead of five when we want to check + if the counter is at a certain value. For 4-LUT architectures this means + we only need one LUT instead of two for each comparison. + 2. We don't need a separate enable signal to turn on and off the counter + between stages, which saves an extra FF and a unique control signal. We + just need to check if cnt_r is not zero to see if the counter is + currently running + */ + if (W == 4) begin + if (i_rf_ready) o_cnt_en <= 1; else + if (o_cnt_done) o_cnt_en <= 0; + o_cnt <= o_cnt + { 2'b0, o_cnt_en }; + end else if (W == 1) begin + o_cnt <= o_cnt + {2'd0,cnt_r[3]}; + cnt_r <= {cnt_r[2:0],(cnt_r[3] & !o_cnt_done) | (i_rf_ready & !o_cnt_en)}; + end + if (i_rst) begin + if (RESET_STRATEGY != "NONE") begin + o_cnt <= 3'd0; + if (W == 1) + cnt_r <= 4'b0000; + else if (W == 4) + o_cnt_en <= 1'b0; + end + end + end + + always @(*) + if (W == 1) + o_cnt_en = |cnt_r; + else if (W == 4) + cnt_r = 4'b1111; + + assign o_ctrl_trap = WITH_CSR & (i_e_op | i_new_irq | misalign_trap_sync); + + generate + if (WITH_CSR) begin : gen_csr + reg misalign_trap_sync_r; + + //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 & !ALIGN) | + (i_dbus_en & i_mem_misalign)); + + always @(posedge i_clk) begin + if (i_ibus_ack | o_cnt_done | i_rst) + misalign_trap_sync_r <= !(i_ibus_ack | i_rst) & ((trap_pending & o_init) | misalign_trap_sync_r); + end + assign misalign_trap_sync = misalign_trap_sync_r; + end else begin : gen_no_csr + assign misalign_trap_sync = 1'b0; + end + endgenerate +endmodule diff --git a/fw/rtl/serv/serv_top.v b/fw/rtl/serv/serv_top.v new file mode 100644 index 0000000..a4e9a90 --- /dev/null +++ b/fw/rtl/serv/serv_top.v @@ -0,0 +1,659 @@ +`default_nettype none + +module serv_top + #(parameter WITH_CSR = 1, + parameter PRE_REGISTER = 1, + parameter RESET_STRATEGY = "MINI", + parameter RESET_PC = 32'd0, + parameter [0:0] MDU = 1'b0, + parameter [0:0] COMPRESSED=0, + parameter [0:0] ALIGN = COMPRESSED) + ( + input wire clk, + input wire i_rst, + input wire i_timer_irq, +`ifdef RISCV_FORMAL + output reg rvfi_valid = 1'b0, + output reg [63:0] rvfi_order = 64'd0, + output reg [31:0] rvfi_insn = 32'd0, + output reg rvfi_trap = 1'b0, + output reg rvfi_halt = 1'b0, + output reg rvfi_intr = 1'b0, + output reg [1:0] rvfi_mode = 2'b11, + output reg [1:0] rvfi_ixl = 2'b01, + output reg [4:0] rvfi_rs1_addr, + output reg [4:0] rvfi_rs2_addr, + output reg [31:0] rvfi_rs1_rdata, + output reg [31:0] rvfi_rs2_rdata, + output reg [4:0] rvfi_rd_addr, + output reg [31:0] rvfi_rd_wdata, + output reg [31:0] rvfi_pc_rdata, + output reg [31:0] rvfi_pc_wdata, + output reg [31:0] rvfi_mem_addr, + output reg [3:0] rvfi_mem_rmask, + output reg [3:0] rvfi_mem_wmask, + output reg [31:0] rvfi_mem_rdata, + output reg [31:0] rvfi_mem_wdata, +`endif + //RF Interface + output wire o_rf_rreq, + output wire o_rf_wreq, + input wire i_rf_ready, + output wire [4+WITH_CSR:0] o_wreg0, + output wire [4+WITH_CSR:0] o_wreg1, + output wire o_wen0, + output wire o_wen1, + output wire o_wdata0, + output wire o_wdata1, + output wire [4+WITH_CSR:0] o_rreg0, + output wire [4+WITH_CSR:0] o_rreg1, + input wire i_rdata0, + input wire i_rdata1, + + 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, + //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; + wire [4:0] rs2_addr; + + wire [3:0] immdec_ctrl; + wire [3:0] immdec_en; + + wire sh_right; + wire bne_or_bge; + wire cond_branch; + wire two_stage_op; + wire e_op; + wire ebreak; + wire branch_op; + wire shift_op; + wire slt_or_branch; + wire rd_op; + wire mdu_op; + + 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; + wire jal_or_jalr; + wire utype; + wire mret; + wire imm; + wire trap; + wire pc_rel; + wire iscomp; + + wire init; + wire cnt_en; + wire cnt0to3; + wire cnt12to31; + wire cnt0; + wire cnt1; + wire cnt2; + wire cnt3; + wire cnt7; + + wire cnt_done; + + wire bufreg_en; + wire bufreg_sh_signed; + wire bufreg_rs1_en; + wire bufreg_imm_en; + wire bufreg_clr_lsb; + wire bufreg_q; + wire bufreg2_q; + wire [31:0] dbus_rdt; + wire dbus_ack; + + wire alu_sub; + wire [1:0] alu_bool_op; + wire alu_cmp_eq; + wire alu_cmp_sig; + wire alu_cmp; + wire [2:0] alu_rd_sel; + + wire rs1; + wire rs2; + wire rd_en; + + wire op_b; + wire op_b_sel; + + wire mem_signed; + wire mem_word; + wire mem_half; + wire [1:0] mem_bytecnt; + wire sh_done; + wire sh_done_r; + wire byte_valid; + + wire mem_misalign; + + wire bad_pc; + + wire csr_mstatus_en; + wire csr_mie_en; + wire csr_mcause_en; + wire [1:0] csr_source; + wire csr_imm; + wire csr_d_sel; + wire csr_en; + wire [1:0] csr_addr; + wire csr_pc; + wire csr_imm_en; + wire csr_in; + wire rf_csr_out; + wire dbus_en; + + wire new_irq; + + wire [1:0] lsb; + + wire [31:0] i_wb_rdt; + + wire [31:0] wb_ibus_adr; + wire wb_ibus_cyc; + wire [31:0] wb_ibus_rdt; + wire wb_ibus_ack; + + generate + if (ALIGN) begin : gen_align + serv_aligner align + ( + .clk(clk), + .rst(i_rst), + // serv_rf_top + .i_ibus_adr(wb_ibus_adr), + .i_ibus_cyc(wb_ibus_cyc), + .o_ibus_rdt(wb_ibus_rdt), + .o_ibus_ack(wb_ibus_ack), + // servant_arbiter + .o_wb_ibus_adr(o_ibus_adr), + .o_wb_ibus_cyc(o_ibus_cyc), + .i_wb_ibus_rdt(i_ibus_rdt), + .i_wb_ibus_ack(i_ibus_ack)); + end else begin : gen_no_align + assign o_ibus_adr = wb_ibus_adr; + assign o_ibus_cyc = wb_ibus_cyc; + assign wb_ibus_rdt = i_ibus_rdt; + assign wb_ibus_ack = i_ibus_ack; + end + endgenerate + + generate + if (COMPRESSED) begin : gen_compressed + serv_compdec compdec + ( + .i_clk(clk), + .i_instr(wb_ibus_rdt), + .i_ack(wb_ibus_ack), + .o_instr(i_wb_rdt), + .o_iscomp(iscomp)); + end else begin : gen_no_compressed + assign i_wb_rdt = wb_ibus_rdt; + assign iscomp = 1'b0; + end + endgenerate + + serv_state + #(.RESET_STRATEGY (RESET_STRATEGY), + .WITH_CSR (WITH_CSR[0:0]), + .MDU(MDU), + .ALIGN(ALIGN)) + state + ( + .i_clk (clk), + .i_rst (i_rst), + //State + .i_new_irq (new_irq), + .i_alu_cmp (alu_cmp), + .o_init (init), + .o_cnt_en (cnt_en), + .o_cnt0to3 (cnt0to3), + .o_cnt12to31 (cnt12to31), + .o_cnt0 (cnt0), + .o_cnt1 (cnt1), + .o_cnt2 (cnt2), + .o_cnt3 (cnt3), + .o_cnt7 (cnt7), + .o_cnt_done (cnt_done), + .o_bufreg_en (bufreg_en), + .o_ctrl_pc_en (ctrl_pc_en), + .o_ctrl_jump (jump), + .o_ctrl_trap (trap), + .i_ctrl_misalign(lsb[1]), + .i_sh_done (sh_done), + .i_sh_done_r (sh_done_r), + .o_mem_bytecnt (mem_bytecnt), + .i_mem_misalign (mem_misalign), + //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_shift_op (shift_op), + .i_sh_right (sh_right), + .i_slt_or_branch (slt_or_branch), + .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), + .o_ibus_cyc (wb_ibus_cyc), + .i_ibus_ack (wb_ibus_ack), + //RF Interface + .o_rf_rreq (o_rf_rreq), + .o_rf_wreq (o_rf_wreq), + .i_rf_ready (i_rf_ready), + .o_rf_rd_en (rd_en)); + + serv_decode + #(.PRE_REGISTER (PRE_REGISTER), + .MDU(MDU)) + decode + ( + .clk (clk), + //Input + .i_wb_rdt (i_wb_rdt[31:2]), + .i_wb_en (wb_ibus_ack), + //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_shift_op (shift_op), + .o_slt_or_branch (slt_or_branch), + .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), + + //To bufreg + .o_bufreg_rs1_en (bufreg_rs1_en), + .o_bufreg_imm_en (bufreg_imm_en), + .o_bufreg_clr_lsb (bufreg_clr_lsb), + .o_bufreg_sh_signed (bufreg_sh_signed), + //To bufreg2 + .o_op_b_source (op_b_sel), + //To ctrl + .o_ctrl_jal_or_jalr (jal_or_jalr), + .o_ctrl_utype (utype), + .o_ctrl_pc_rel (pc_rel), + .o_ctrl_mret (mret), + //To alu + .o_alu_sub (alu_sub), + .o_alu_bool_op (alu_bool_op), + .o_alu_cmp_eq (alu_cmp_eq), + .o_alu_cmp_sig (alu_cmp_sig), + .o_alu_rd_sel (alu_rd_sel), + //To mem IF + .o_mem_cmd (o_dbus_we), + .o_mem_signed (mem_signed), + .o_mem_word (mem_word), + .o_mem_half (mem_half), + //To CSR + .o_csr_en (csr_en), + .o_csr_addr (csr_addr), + .o_csr_mstatus_en (csr_mstatus_en), + .o_csr_mie_en (csr_mie_en), + .o_csr_mcause_en (csr_mcause_en), + .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)); + + serv_immdec immdec + ( + .i_clk (clk), + //State + .i_cnt_en (cnt_en), + .i_cnt_done (cnt_done), + //Control + .i_immdec_en (immdec_en), + .i_csr_imm_en (csr_imm_en), + .i_ctrl (immdec_ctrl), + .o_rd_addr (rd_addr), + .o_rs1_addr (rs1_addr), + .o_rs2_addr (rs2_addr), + //Data + .o_csr_imm (csr_imm), + .o_imm (imm), + //External + .i_wb_en (wb_ibus_ack), + .i_wb_rdt (i_wb_rdt[31:7])); + + serv_bufreg + #(.MDU(MDU)) + bufreg + ( + .i_clk (clk), + //State + .i_cnt0 (cnt0), + .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), + .i_rs1_en (bufreg_rs1_en), + .i_imm_en (bufreg_imm_en), + .i_clr_lsb (bufreg_clr_lsb), + //Data + .i_rs1 (rs1), + .i_imm (imm), + .o_q (bufreg_q), + //External + .o_dbus_adr (o_dbus_adr), + .o_ext_rs1 (o_ext_rs1)); + + serv_bufreg2 bufreg2 + ( + .i_clk (clk), + //State + .i_en (cnt_en), + .i_init (init), + .i_cnt_done (cnt_done), + .i_lsb (lsb), + .i_byte_valid (byte_valid), + .o_sh_done (sh_done), + .o_sh_done_r (sh_done_r), + //Control + .i_op_b_sel (op_b_sel), + .i_shift_op (shift_op), + //Data + .i_rs2 (rs2), + .i_imm (imm), + .o_op_b (op_b), + .o_q (bufreg2_q), + //External + .o_dat (o_dbus_dat), + .i_load (dbus_ack), + .i_dat (dbus_rdt)); + + serv_ctrl + #(.RESET_PC (RESET_PC), + .RESET_STRATEGY (RESET_STRATEGY), + .WITH_CSR (WITH_CSR)) + ctrl + ( + .clk (clk), + .i_rst (i_rst), + //State + .i_pc_en (ctrl_pc_en), + .i_cnt12to31 (cnt12to31), + .i_cnt0 (cnt0), + .i_cnt1 (cnt1), + .i_cnt2 (cnt2), + //Control + .i_jump (jump), + .i_jal_or_jalr (jal_or_jalr), + .i_utype (utype), + .i_pc_rel (pc_rel), + .i_trap (trap | mret), + .i_iscomp (iscomp), + //Data + .i_imm (imm), + .i_buf (bufreg_q), + .i_csr_pc (csr_pc), + .o_rd (ctrl_rd), + .o_bad_pc (bad_pc), + //External + .o_ibus_adr (wb_ibus_adr)); + + serv_alu alu + ( + .clk (clk), + //State + .i_en (cnt_en), + .i_cnt0 (cnt0), + .o_cmp (alu_cmp), + //Control + .i_sub (alu_sub), + .i_bool_op (alu_bool_op), + .i_cmp_eq (alu_cmp_eq), + .i_cmp_sig (alu_cmp_sig), + .i_rd_sel (alu_rd_sel), + //Data + .i_rs1 (rs1), + .i_op_b (op_b), + .i_buf (bufreg_q), + .o_rd (alu_rd)); + + serv_rf_if + #(.WITH_CSR (WITH_CSR)) + rf_if + (//RF interface + .i_cnt_en (cnt_en), + .o_wreg0 (o_wreg0), + .o_wreg1 (o_wreg1), + .o_wen0 (o_wen0), + .o_wen1 (o_wen1), + .o_wdata0 (o_wdata0), + .o_wdata1 (o_wdata1), + .o_rreg0 (o_rreg0), + .o_rreg1 (o_rreg1), + .i_rdata0 (i_rdata0), + .i_rdata1 (i_rdata1), + + //Trap interface + .i_trap (trap), + .i_mret (mret), + .i_mepc (wb_ibus_adr[0]), + .i_mtval_pc (mtval_pc), + .i_bufreg_q (bufreg_q), + .i_bad_pc (bad_pc), + .o_csr_pc (csr_pc), + //CSR write port + .i_csr_en (csr_en), + .i_csr_addr (csr_addr), + .i_csr (csr_in), + //RD write port + .i_rd_wen (rd_en), + .i_rd_waddr (rd_addr), + .i_ctrl_rd (ctrl_rd), + .i_alu_rd (alu_rd), + .i_rd_alu_en (rd_alu_en), + .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), + .o_rs1 (rs1), + //RS2 read port + .i_rs2_raddr (rs2_addr), + .o_rs2 (rs2), + + //CSR read port + .o_csr (rf_csr_out)); + + serv_mem_if + #(.WITH_CSR (WITH_CSR[0:0])) + mem_if + ( + .i_clk (clk), + //State + .i_bytecnt (mem_bytecnt), + .i_lsb (lsb), + .o_byte_valid (byte_valid), + .o_misalign (mem_misalign), + //Control + .i_mdu_op (mdu_op), + .i_signed (mem_signed), + .i_word (mem_word), + .i_half (mem_half), + //Data + .i_bufreg2_q (bufreg2_q), + .o_rd (mem_rd), + //External interface + .o_wb_sel (o_dbus_sel)); + + generate + if (|WITH_CSR) begin : gen_csr + serv_csr + #(.RESET_STRATEGY (RESET_STRATEGY)) + csr + ( + .i_clk (clk), + .i_rst (i_rst), + //State + .i_trig_irq (wb_ibus_ack), + .i_en (cnt_en), + .i_cnt0to3 (cnt0to3), + .i_cnt3 (cnt3), + .i_cnt7 (cnt7), + .i_cnt_done (cnt_done), + .i_mem_op (!mtval_pc), + .i_mtip (i_timer_irq), + .i_trap (trap), + .o_new_irq (new_irq), + //Control + .i_e_op (e_op), + .i_ebreak (ebreak), + .i_mem_cmd (o_dbus_we), + .i_mstatus_en (csr_mstatus_en), + .i_mie_en (csr_mie_en ), + .i_mcause_en (csr_mcause_en ), + .i_csr_source (csr_source), + .i_mret (mret), + .i_csr_d_sel (csr_d_sel), + //Data + .i_rf_csr_out (rf_csr_out), + .o_csr_in (csr_in), + .i_csr_imm (csr_imm), + .i_rs1 (rs1), + .o_q (csr_rd)); + end else begin : gen_no_csr + assign csr_in = 1'b0; + assign csr_rd = 1'b0; + assign new_irq = 1'b0; + end + endgenerate + + +`ifdef RISCV_FORMAL + reg [31:0] pc = RESET_PC; + + wire rs_en = two_stage_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 (wb_ibus_cyc & wb_ibus_ack) + rvfi_insn <= i_wb_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 + rvfi_rd_addr <= 5'd0; + rvfi_rd_wdata <= 32'd0; + end + end + rvfi_trap <= trap; + if (rvfi_valid) begin + rvfi_trap <= 1'b0; + 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 <= !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 <= {!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 + rvfi_mem_addr <= o_dbus_adr; + rvfi_mem_rmask <= o_dbus_we ? 4'b0000 : o_dbus_sel; + rvfi_mem_wmask <= o_dbus_we ? o_dbus_sel : 4'b0000; + rvfi_mem_rdata <= i_dbus_rdt; + rvfi_mem_wdata <= o_dbus_dat; + end + if (wb_ibus_ack) begin + rvfi_mem_rmask <= 4'b0000; + rvfi_mem_wmask <= 4'b0000; + end + end + /* verilator lint_off COMBDLY */ + always @(wb_ibus_adr) + rvfi_pc_wdata <= wb_ibus_adr; + /* verilator lint_on COMBDLY */ + + +`endif + +generate + if (MDU) begin: gen_mdu + assign dbus_rdt = i_ext_ready ? i_ext_rd:i_dbus_rdt; + assign dbus_ack = i_dbus_ack | i_ext_ready; + end else begin : gen_no_mdu + 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/fw/rtl/top.sv b/fw/rtl/top.sv index 7cad9ad..d00ac60 100644 --- a/fw/rtl/top.sv +++ b/fw/rtl/top.sv @@ -14,6 +14,9 @@ module top ( input n64_si_clk, inout n64_si_dq, + input n64_cic_clk, + inout n64_cic_dq, + input usb_pwrsav, output usb_clk, output usb_cs, @@ -48,8 +51,6 @@ module top ( output mcu_miso, // Unused I/O - output n64_cic_clk, - output n64_cic_dq, output n64_video_sync ); @@ -137,7 +138,10 @@ module top ( .n64_pi_ad(n64_pi_ad), .n64_si_clk(n64_si_clk), - .n64_si_dq(n64_si_dq) + .n64_si_dq(n64_si_dq), + + .n64_cic_clk(n64_cic_clk), + .n64_cic_dq(n64_cic_dq) ); @@ -272,8 +276,6 @@ module top ( // Unused I/O - assign n64_cic_clk = 1'bZ; - assign n64_cic_dq = 1'bZ; assign n64_video_sync = 1'bZ; endmodule diff --git a/sw/bootloader/Makefile b/sw/bootloader/Makefile index aa0a74b..2f97b5a 100644 --- a/sw/bootloader/Makefile +++ b/sw/bootloader/Makefile @@ -1,4 +1,4 @@ -TOOLCHAIN = mips64-elf- +TOOLCHAIN = $(N64_INST)/bin/mips64-elf- CC = $(TOOLCHAIN)gcc CXX = $(TOOLCHAIN)g++ OBJCOPY = $(TOOLCHAIN)objcopy diff --git a/sw/cic/.gitignore b/sw/cic/.gitignore new file mode 100644 index 0000000..58e68f7 --- /dev/null +++ b/sw/cic/.gitignore @@ -0,0 +1,4 @@ +*.bin +*.elf +*.lst +*.mem diff --git a/sw/cic/build.sh b/sw/cic/build.sh new file mode 100755 index 0000000..5e370a8 --- /dev/null +++ b/sw/cic/build.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +TOOLCHAIN="riscv32-unknown-elf-" +CFLAGS=" \ + -march=rv32i \ + -mabi=ilp32 \ + -Os \ + -Wl,--gc-sections \ + -ffunction-sections \ + -fdata-sections \ + -ffreestanding \ + -nostartfiles \ + -nostdlib \ + -nodefaultlibs \ + -fno-builtin \ + -mcmodel=medany \ +" + +case "$1" in + all) + ${TOOLCHAIN}gcc $CFLAGS -T cic.ld -o cic.elf startup.S cic.c + echo "Size of cic:" + ${TOOLCHAIN}size -B -d cic.elf + ${TOOLCHAIN}objdump -S -D cic.elf > cic.lst + ${TOOLCHAIN}objcopy -O binary cic.elf cic.bin + python3 ./convert.py cic.bin cic.mem + ;; + clean) + rm -f cic.elf cic.lst cic.bin cic.mem + ;; +esac diff --git a/sw/cic/cic.c b/sw/cic/cic.c new file mode 100644 index 0000000..0da7fc8 --- /dev/null +++ b/sw/cic/cic.c @@ -0,0 +1,361 @@ +// Original code sourced from https://github.com/jago85/UltraCIC_C + +// MIT License + +// Copyright (c) 2019 Jan Goldacker +// Copyright (c) 2022-2023 Mateusz Faderewski + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include +#include +#include + + +typedef struct { + volatile uint32_t CIC_CONFIG[2]; + volatile uint32_t GPIO; +} ext_regs_t; + +#define EXT ((ext_regs_t *) (0xC0000000UL)) + +#define CIC_DQ (1 << 0) +#define CIC_CLK (1 << 1) +#define CIC_RESET (1 << 2) +#define CIC_INVALID_REGION (1 << 3) + +#define CIC_IS_RUNNING() (EXT->GPIO & CIC_RESET) +#define CIC_CLK_WAIT_LOW() { while ((EXT->GPIO & (CIC_RESET | CIC_CLK)) == (CIC_RESET | CIC_CLK)); } +#define CIC_CLK_WAIT_HIGH() { while ((EXT->GPIO & (CIC_RESET | CIC_CLK)) == CIC_RESET); } +#define CIC_DQ_GET() (EXT->GPIO & CIC_DQ) +#define CIC_DQ_SET(v) { EXT->GPIO = ((v) ? CIC_DQ : 0); } +#define CIC_CLK_GET() (EXT->GPIO & (CIC_RESET | CIC_CLK)) +#define CIC_NOTIFY_INVALID_REGION() { EXT->GPIO = (CIC_INVALID_REGION | CIC_DQ); } + + +typedef struct { + bool cic_disabled; + bool cic_64dd_mode; + bool cic_region; + uint8_t cic_seed; + uint8_t cic_checksum[6]; +} cic_config_t; + +static cic_config_t config; + +static uint8_t cic_ram[32]; +static uint8_t cic_x105_ram[30]; + +static const uint8_t cic_ram_init[2][32] = {{ + 0xE, 0x0, 0x9, 0xA, 0x1, 0x8, 0x5, 0xA, 0x1, 0x3, 0xE, 0x1, 0x0, 0xD, 0xE, 0xC, + 0x0, 0xB, 0x1, 0x4, 0xF, 0x8, 0xB, 0x5, 0x7, 0xC, 0xD, 0x6, 0x1, 0xE, 0x9, 0x8 +}, { + 0xE, 0x0, 0x4, 0xF, 0x5, 0x1, 0x2, 0x1, 0x7, 0x1, 0x9, 0x8, 0x5, 0x7, 0x5, 0xA, + 0x0, 0xB, 0x1, 0x2, 0x3, 0xF, 0x8, 0x2, 0x7, 0x1, 0x9, 0x8, 0x1, 0x1, 0x5, 0xC +}}; + + +static void cic_die (void) { + while (CIC_IS_RUNNING()); +} + +static void cic_init (void) { + CIC_DQ_SET(1); + + while (!CIC_IS_RUNNING()); + + uint32_t cic_config[2]; + + cic_config[0] = EXT->CIC_CONFIG[0]; + cic_config[1] = EXT->CIC_CONFIG[1]; + + config.cic_disabled = (cic_config[0] & (1 << 26)); + config.cic_64dd_mode = (cic_config[0] & (1 << 25)); + config.cic_region = (cic_config[0] & (1 << 24)); + config.cic_seed = ((cic_config[0] >> 16) & 0xFF); + config.cic_checksum[0] = ((cic_config[0] >> 8) & 0xFF); + config.cic_checksum[1] = (cic_config[0] & 0xFF); + config.cic_checksum[2] = ((cic_config[1] >> 24) & 0xFF); + config.cic_checksum[3] = ((cic_config[1] >> 16) & 0xFF); + config.cic_checksum[4] = ((cic_config[1] >> 8) & 0xFF); + config.cic_checksum[5] = (cic_config[1] & 0xFF); + + if (config.cic_disabled) { + cic_die(); + } +} + +static uint8_t cic_read (void) { + uint8_t value; + CIC_CLK_WAIT_LOW(); + value = CIC_DQ_GET() ? 1 : 0; + CIC_CLK_WAIT_HIGH(); + return value; +} + +static void cic_write (uint8_t value) { + CIC_CLK_WAIT_LOW(); + CIC_DQ_SET(value); + CIC_CLK_WAIT_HIGH(); + CIC_DQ_SET(1); +} + +static uint8_t cic_read_nibble (void) { + uint8_t data = 0; + for (int i = 0; i < 4; i++) { + data = ((data << 1) | cic_read()); + } + return data; +} + +static void cic_write_nibble (uint8_t data) { + cic_write(data & 0x08); + cic_write(data & 0x04); + cic_write(data & 0x02); + cic_write(data & 0x01); +} + +static void cic_write_ram_nibbles (uint8_t index) { + do { + cic_write_nibble(cic_ram[index++]); + } while ((index & 0x0F) != 0); +} + +static void cic_encode_round (uint8_t index) { + uint8_t data = cic_ram[index++]; + do { + data = ((((data + 1) & 0x0F) + cic_ram[index]) & 0x0F); + cic_ram[index++] = data; + } while ((index & 0x0F) != 0); +} + +static void cic_write_id (void) { + if (config.cic_64dd_mode) { + CIC_CLK_WAIT_LOW(); + while (CIC_CLK_GET() == CIC_RESET) { + if (!CIC_DQ_GET()) { + cic_die(); + } + } + } else { + cic_write(0); + } + cic_write(config.cic_region ? 1 : 0); + cic_write(0); + cic_write(1); +} + +static void cic_write_seed (void) { + cic_ram[0x0A] = 0x0B; + cic_ram[0x0B] = 0x05; + cic_ram[0x0C] = (config.cic_seed >> 4); + cic_ram[0x0D] = config.cic_seed; + cic_ram[0x0E] = (config.cic_seed >> 4); + cic_ram[0x0F] = config.cic_seed; + cic_encode_round(0x0A); + cic_encode_round(0x0A); + + uint32_t timeout = 100000; + do { + if (timeout == 0) { + CIC_NOTIFY_INVALID_REGION(); + cic_die(); + } + } while (timeout-- && (CIC_CLK_GET() == (CIC_RESET | CIC_CLK))); + + cic_write_ram_nibbles(0x0A); +} + +static void cic_write_checksum (void) { + for (int i = 0; i < 4; i++) { + cic_ram[i] = 0x00; + } + for (int i = 0; i < 6; i++) { + cic_ram[(i * 2) + 4] = ((config.cic_checksum[i] >> 4) & 0x0F); + cic_ram[(i * 2) + 5] = (config.cic_checksum[i] & 0x0F); + } + cic_encode_round(0x00); + cic_encode_round(0x00); + cic_encode_round(0x00); + cic_encode_round(0x00); + cic_write(0); + cic_write_ram_nibbles(0x00); +} + +static void cic_init_ram (void) { + for (int i = 0; i < 32; i++) { + cic_ram[i] = cic_ram_init[config.cic_region ? 1 : 0][i]; + } + cic_ram[0x01] = cic_read_nibble(); + cic_ram[0x11] = cic_read_nibble(); +} + +static void cic_exchange_bytes (uint8_t *a, uint8_t *b) { + uint8_t tmp = *a; + *a = *b; + *b = tmp; +} + +static void cic_round (uint8_t *m) { + uint8_t a, b, x; + + x = m[15]; + a = x; + + do { + b = 1; + a += (m[b] + 1); + m[b] = a; + b++; + a += (m[b] + 1); + cic_exchange_bytes(&a, &m[b]); + m[b] = ~(m[b]); + b++; + a &= 0x0F; + a += ((m[b] & 0x0F) + 1); + if (a < 16) { + cic_exchange_bytes(&a, &m[b]); + b++; + } + a += m[b]; + m[b] = a; + b++; + a += m[b]; + cic_exchange_bytes(&a, &m[b]); + b++; + a &= 0x0F; + a += 8; + if (a < 16) { + a += m[b]; + } + cic_exchange_bytes(&a, &m[b]); + b++; + do { + a += (m[b] + 1); + m[b] = a; + b++; + b &= 0x0F; + } while (b != 0); + a = (x + 0x0F); + x = (a & 0x0F); + } while (x != 0x0F); +} + +static void cic_compare_mode (void) { + cic_round(&cic_ram[0x10]); + cic_round(&cic_ram[0x10]); + cic_round(&cic_ram[0x10]); + + uint8_t index = (cic_ram[0x17] & 0x0F); + if (index == 0) { + index = 1; + } + index |= 0x10; + + do { + cic_read(); + cic_write(cic_ram[index] & 0x01); + if (config.cic_region) { + index--; + } else { + index++; + } + } while (index & 0x0F); +} + +static void cic_x105_algorithm (void) { + uint8_t a = 5; + uint8_t carry = 1; + + for (int i = 0; i < 30; ++i) { + if (!(cic_x105_ram[i] & 0x01)) { + a += 8; + } + if (!(a & 0x02)) { + a += 4; + } + a = ((a + cic_x105_ram[i]) & 0x0F); + cic_x105_ram[i] = a; + if (!carry) { + a += 7; + } + a = ((a + cic_x105_ram[i]) & 0x0F); + a = (a + cic_x105_ram[i] + carry); + if (a >= 0x10) { + carry = 1; + a -= 0x10; + } else { + carry = 0; + } + a = (~(a) & 0x0F); + cic_x105_ram[i] = a; + } +} + +static void cic_x105_mode (void) { + cic_write_nibble(0x0A); + cic_write_nibble(0x0A); + + for (int i = 0; i < 30; i++) { + cic_x105_ram[i] = cic_read_nibble(); + } + + cic_x105_algorithm(); + + cic_write(0); + + for (int i = 0; i < 30; i++) { + cic_write_nibble(cic_x105_ram[i]); + } +} + +static void cic_soft_reset (void) { + volatile uint32_t timeout = 100000; + CIC_CLK_WAIT_LOW(); + while ((timeout--) && CIC_IS_RUNNING()); + cic_write(0); +} + + +__attribute__((naked)) void cic_main (void) { + while (true) { + cic_init(); + + cic_write_id(); + cic_write_seed(); + cic_write_checksum(); + cic_init_ram(); + + while (CIC_IS_RUNNING()) { + uint8_t cmd = 0; + cmd |= (cic_read() << 1); + cmd |= cic_read(); + + if (cmd == 0) { + cic_compare_mode(); + } else if (cmd == 2) { + cic_x105_mode(); + } else if (cmd == 3) { + cic_soft_reset(); + } else { + cic_die(); + } + } + } +} diff --git a/sw/cic/cic.ld b/sw/cic/cic.ld new file mode 100644 index 0000000..466bfb6 --- /dev/null +++ b/sw/cic/cic.ld @@ -0,0 +1,51 @@ +OUTPUT_ARCH("riscv") +OUTPUT_FORMAT("elf32-littleriscv") + +MEMORY { + ram (rwx) : org = 0x80000000, len = 2k +} + +ENTRY(entry_handler) + +SECTIONS { + .text : { + *(.text.entry_handler) + *(.text .text.* .gnu.linkonce.t.*) + . = ALIGN(4); + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.rodata1) + . = ALIGN(4); + } > ram : text + + .data : { + . = ALIGN(4); + *(.sdata2 .sdata2.* .gnu.linkonce.s2.*) + *(.data1) + *(.data .data.* .gnu.linkonce.d.*) + . = ALIGN(4); + } > ram : data + + .bss : { + . = ALIGN(4); + _sbss = .; + *(.dynsbss) + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.sbss2 .sbss2.* .gnu.linkonce.sb2.*) + *(.tbss .tbss.* .gnu.linkonce.tb.*) + *(.tcommon) + *(.scommon) + *(.dynbss) + *(.bss .bss.* .gnu.linkonce.b.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > ram : bss + + _sp = ORIGIN(ram) + LENGTH(ram); +} + +PHDRS { + text PT_LOAD FLAGS(5); + data PT_LOAD FLAGS(6); + bss PT_LOAD FLAGS(6); +} diff --git a/sw/cic/convert.py b/sw/cic/convert.py new file mode 100644 index 0000000..ae55057 --- /dev/null +++ b/sw/cic/convert.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import sys + + + +if __name__ == '__main__': + if (len(sys.argv) != 3): + print(f'Usage: python {sys.argv[0]} in_file out_file') + sys.exit(1) + + with open(sys.argv[1], 'rb') as f: + output = '' + while True: + file_bytes = f.read(4) + if len(file_bytes) != 4: + break + output += f"{int.from_bytes(file_bytes, 'little'):08X}\n" + + with open(sys.argv[2], 'w') as f: + f.write(output) diff --git a/sw/cic/startup.S b/sw/cic/startup.S new file mode 100644 index 0000000..ffc9a79 --- /dev/null +++ b/sw/cic/startup.S @@ -0,0 +1,23 @@ +.option norvc +.section .text.entry_handler +entry_handler: + .global entry_handler + +init_stack_pointer: + .option push + .option norelax + la sp, _sp + .option pop + +init_bss: + la t5, _sbss + la t6, _ebss + beq a0, a1, 2f +1: + sw zero, 0(t5) + addi t5, t5, 4 + bltu t5, t6, 1b +2: + +run_main: + tail cic_main diff --git a/sw/controller/common.mk b/sw/controller/common.mk index 8dbbcf1..7fee96b 100644 --- a/sw/controller/common.mk +++ b/sw/controller/common.mk @@ -6,7 +6,7 @@ OBJDUMP = $(TOOLCHAIN)objdump SIZE = $(TOOLCHAIN)size FLAGS = -mcpu=cortex-m0plus -mthumb -DSTM32G030xx $(USER_FLAGS) -g -ggdb3 -CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP -I./inc +CFLAGS = -Os -Wall -ffunction-sections -fdata-sections -ffreestanding -MMD -MP -I. -isystem ./inc LDFLAGS = -nostartfiles -Wl,--gc-sections SRC_DIR = src diff --git a/sw/controller/src/app.S b/sw/controller/src/app.S index 5150fde..01b6ea0 100644 --- a/sw/controller/src/app.S +++ b/sw/controller/src/app.S @@ -7,7 +7,7 @@ .section .loader, "a", %progbits .type loader, %object loader: - .incbin "../build/loader/loader.bin" + .incbin "build/loader/loader.bin" .section .text.Reset_Handler diff --git a/sw/controller/src/app.c b/sw/controller/src/app.c index 1988a88..16be2f7 100644 --- a/sw/controller/src/app.c +++ b/sw/controller/src/app.c @@ -1,5 +1,4 @@ #include "app.h" -#include "cic.h" #include "gvr.h" #include "hw.h" #include "led.h" @@ -7,20 +6,18 @@ #include "task.h" -#define CIC_STACK_SIZE (256) #define RTC_STACK_SIZE (256) #define LED_STACK_SIZE (256) #define GVR_STACK_SIZE (2048) -uint8_t cic_stack[CIC_STACK_SIZE] __attribute__((aligned(8))); uint8_t rtc_stack[RTC_STACK_SIZE] __attribute__((aligned(8))); uint8_t led_stack[LED_STACK_SIZE] __attribute__((aligned(8))); uint8_t gvr_stack[GVR_STACK_SIZE] __attribute__((aligned(8))); void app_get_stack_usage (uint32_t *usage) { - *usage++ = task_get_stack_usage(cic_stack, CIC_STACK_SIZE); + *usage++ = 0; *usage++ = task_get_stack_usage(rtc_stack, RTC_STACK_SIZE); *usage++ = task_get_stack_usage(led_stack, LED_STACK_SIZE); *usage++ = task_get_stack_usage(gvr_stack, GVR_STACK_SIZE); @@ -28,9 +25,7 @@ void app_get_stack_usage (uint32_t *usage) { void app (void) { hw_init(); - cic_hw_init(); - task_create(TASK_ID_CIC, cic_task, cic_stack, CIC_STACK_SIZE); task_create(TASK_ID_RTC, rtc_task, rtc_stack, RTC_STACK_SIZE); task_create(TASK_ID_LED, led_task, led_stack, LED_STACK_SIZE); task_create(TASK_ID_GVR, gvr_task, gvr_stack, GVR_STACK_SIZE); diff --git a/sw/controller/src/cic.c b/sw/controller/src/cic.c index 5961946..a676232 100644 --- a/sw/controller/src/cic.c +++ b/sw/controller/src/cic.c @@ -1,34 +1,8 @@ -// Original code sourced from https://github.com/jago85/UltraCIC_C - -// MIT License - -// Copyright (c) 2019 Jan Goldacker -// Copyright (c) 2022 Mateusz Faderewski - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - - #include "cic.h" +#include "fpga.h" #include "hw.h" #include "led.h" #include "rtc.h" -#include "task.h" typedef enum { @@ -38,372 +12,78 @@ typedef enum { } cic_region_t; -static volatile bool cic_enabled = false; -static volatile bool cic_detect_enabled; - -static volatile uint8_t cic_next_rd; -static volatile uint8_t cic_next_wr; - -static volatile bool cic_disabled = false; -static volatile bool cic_dd_mode = false; -static volatile uint8_t cic_seed = 0x3F; -static volatile uint8_t cic_checksum[6] = { 0xA5, 0x36, 0xC0, 0xF1, 0xD8, 0x59 }; - -static uint8_t cic_ram[32]; -static uint8_t cic_x105_ram[30]; - -static const uint8_t cic_ram_init[2][32] = {{ - 0x0E, 0x00, 0x09, 0x0A, 0x01, 0x08, 0x05, 0x0A, 0x01, 0x03, 0x0E, 0x01, 0x00, 0x0D, 0x0E, 0x0C, - 0x00, 0x0B, 0x01, 0x04, 0x0F, 0x08, 0x0B, 0x05, 0x07, 0x0C, 0x0D, 0x06, 0x01, 0x0E, 0x09, 0x08 -}, { - 0x0E, 0x00, 0x04, 0x0F, 0x05, 0x01, 0x02, 0x01, 0x07, 0x01, 0x09, 0x08, 0x05, 0x07, 0x05, 0x0A, - 0x00, 0x0B, 0x01, 0x02, 0x03, 0x0F, 0x08, 0x02, 0x07, 0x01, 0x09, 0x08, 0x01, 0x01, 0x05, 0x0C -}}; - - static void cic_irq_reset_falling (void) { - cic_enabled = false; - hw_gpio_set(GPIO_ID_N64_CIC_DQ); led_clear_error(LED_ERROR_CIC); } -static void cic_irq_reset_rising (void) { - if (!cic_disabled) { - cic_enabled = true; - task_set_ready_and_reset(TASK_ID_CIC); - } -} - -static void cic_irq_clk_falling (void) { - if (cic_enabled) { - if (!cic_next_wr) { - hw_gpio_reset(GPIO_ID_N64_CIC_DQ); - } - cic_next_rd = hw_gpio_get(GPIO_ID_N64_CIC_DQ) ? 1 : 0; - task_set_ready(TASK_ID_CIC); - } -} - -static void cic_irq_clk_rising (void) { - hw_gpio_set(GPIO_ID_N64_CIC_DQ); - if (cic_detect_enabled) { - cic_detect_enabled = false; - if (!hw_gpio_get(GPIO_ID_N64_CIC_DQ)) { - cic_enabled = false; - } - } -} - -static uint8_t cic_read (void) { - cic_next_wr = 1; - task_yield(); - return cic_next_rd; -} - -static void cic_write (uint8_t bit) { - cic_next_wr = bit; - task_yield(); -} - -static void cic_start_detect (void) { - cic_detect_enabled = cic_dd_mode; -} - -static uint8_t cic_read_nibble (void) { - uint8_t data = 0; - for (int i = 0; i < 4; i++) { - data = ((data << 1) | cic_read()); - } - return data; -} - -static void cic_write_nibble (uint8_t data) { - cic_write(data & 0x08); - cic_write(data & 0x04); - cic_write(data & 0x02); - cic_write(data & 0x01); -} - -static void cic_write_ram_nibbles (uint8_t index) { - do { - cic_write_nibble(cic_ram[index++]); - } while ((index & 0x0F) != 0); -} - -static void cic_encode_round (uint8_t index) { - uint8_t data = cic_ram[index++]; - do { - data = ((((data + 1) & 0x0F) + cic_ram[index]) & 0x0F); - cic_ram[index++] = data; - } while ((index & 0x0F) != 0); -} - -static void cic_write_id (cic_region_t region) { - cic_start_detect(); - cic_write(cic_dd_mode ? 1 : 0); - cic_write(region == REGION_PAL ? 1 : 0); - cic_write(0); - cic_write(1); -} - -static void cic_write_id_failed (void) { - uint8_t current_region = rtc_get_region(); - uint8_t next_region = (current_region == REGION_NTSC) ? REGION_PAL : REGION_NTSC; - rtc_set_region(next_region); - led_blink_error(LED_ERROR_CIC); -} - -static void cic_write_seed (void) { - cic_ram[0x0A] = 0x0B; - cic_ram[0x0B] = 0x05; - cic_ram[0x0C] = (cic_seed >> 4); - cic_ram[0x0D] = cic_seed; - cic_ram[0x0E] = (cic_seed >> 4); - cic_ram[0x0F] = cic_seed; - cic_encode_round(0x0A); - cic_encode_round(0x0A); - cic_write_ram_nibbles(0x0A); -} - -static void cic_write_checksum (void) { - for (int i = 0; i < 4; i++) { - cic_ram[i] = 0x00; - } - for (int i = 0; i < 6; i++) { - cic_ram[(i * 2) + 4] = ((cic_checksum[i] >> 4) & 0x0F); - cic_ram[(i * 2) + 5] = (cic_checksum[i] & 0x0F); - } - cic_encode_round(0x00); - cic_encode_round(0x00); - cic_encode_round(0x00); - cic_encode_round(0x00); - cic_write(0); - cic_write_ram_nibbles(0x00); -} - -static void cic_init_ram (cic_region_t region) { - if (region < __REGION_MAX) { - for (int i = 0; i < 32; i++) { - cic_ram[i] = cic_ram_init[region][i]; - } - } - cic_ram[0x01] = cic_read_nibble(); - cic_ram[0x11] = cic_read_nibble(); -} - -static void cic_exchange_bytes (uint8_t *a, uint8_t *b) { - uint8_t tmp = *a; - *a = *b; - *b = tmp; -} - -static void cic_round (uint8_t *m) { - uint8_t a, b, x; - - x = m[15]; - a = x; - - do { - b = 1; - a += (m[b] + 1); - m[b] = a; - b++; - a += (m[b] + 1); - cic_exchange_bytes(&a, &m[b]); - m[b] = ~(m[b]); - b++; - a &= 0x0F; - a += ((m[b] & 0x0F) + 1); - if (a < 16) { - cic_exchange_bytes(&a, &m[b]); - b++; - } - a += m[b]; - m[b] = a; - b++; - a += m[b]; - cic_exchange_bytes(&a, &m[b]); - b++; - a &= 0x0F; - a += 8; - if (a < 16) { - a += m[b]; - } - cic_exchange_bytes(&a, &m[b]); - b++; - do { - a += (m[b] + 1); - m[b] = a; - b++; - b &= 0x0F; - } while (b != 0); - a = (x + 0x0F); - x = (a & 0x0F); - } while (x != 0x0F); -} - -static void cic_compare_mode (cic_region_t region) { - cic_round(&cic_ram[0x10]); - cic_round(&cic_ram[0x10]); - cic_round(&cic_ram[0x10]); - - uint8_t index = (cic_ram[0x17] & 0x0F); - if (index == 0) { - index = 1; - } - index |= 0x10; - - do { - cic_read(); - cic_write(cic_ram[index] & 0x01); - if (region == REGION_PAL) { - index--; - } else { - index++; - } - } while (index & 0x0F); -} - -static void cic_x105_algorithm (void) { - uint8_t a = 5; - uint8_t carry = 1; - - for (int i = 0; i < 30; ++i) { - if (!(cic_x105_ram[i] & 0x01)) { - a += 8; - } - if (!(a & 0x02)) { - a += 4; - } - a = ((a + cic_x105_ram[i]) & 0x0F); - cic_x105_ram[i] = a; - if (!carry) { - a += 7; - } - a = ((a + cic_x105_ram[i]) & 0x0F); - a = (a + cic_x105_ram[i] + carry); - if (a >= 0x10) { - carry = 1; - a -= 0x10; - } else { - carry = 0; - } - a = (~(a) & 0x0F); - cic_x105_ram[i] = a; - } -} - -static void cic_x105_mode (void) { - cic_write_nibble(0x0A); - cic_write_nibble(0x0A); - - for (int i = 0; i < 30; i++) { - cic_x105_ram[i] = cic_read_nibble(); - } - - cic_x105_algorithm(); - - cic_write(0); - - for (int i = 0; i < 30; i++) { - cic_write_nibble(cic_x105_ram[i]); - } -} - -static void cic_soft_reset_timeout (void) { - hw_gpio_reset(GPIO_ID_N64_CIC_DQ); - task_set_ready(TASK_ID_CIC); -} - -static void cic_soft_reset (void) { - cic_read(); - hw_tim_setup(TIM_ID_CIC, 500, cic_soft_reset_timeout); - task_yield(); -} - void cic_reset_parameters (void) { - cic_disabled = false; - cic_dd_mode = false; - cic_seed = 0x3F; - cic_checksum[0] = 0xA5; - cic_checksum[1] = 0x36; - cic_checksum[2] = 0xC0; - cic_checksum[3] = 0xF1; - cic_checksum[4] = 0xD8; - cic_checksum[5] = 0x59; + cic_region_t region = rtc_get_region(); + + const uint8_t default_seed = 0x3F; + const uint64_t default_checksum = 0xA536C0F1D859ULL; + + uint32_t cic_config_0 = (default_seed << CIC_SEED_BIT) | ((default_checksum >> 32) & 0xFFFF); + uint32_t cic_config_1 = (default_checksum & 0xFFFFFFFFUL); + + if (region == REGION_PAL) { + cic_config_0 |= CIC_REGION; + } + + fpga_reg_set(REG_CIC_0, cic_config_0); + fpga_reg_set(REG_CIC_1, cic_config_1); } void cic_set_parameters (uint32_t *args) { - cic_disabled = (args[0] >> 24) & (1 << 0); - cic_seed = (args[0] >> 16) & 0xFF; - cic_checksum[0] = (args[0] >> 8) & 0xFF; - cic_checksum[1] = args[0] & 0xFF; - cic_checksum[2] = (args[1] >> 24) & 0xFF; - cic_checksum[3] = (args[1] >> 16) & 0xFF; - cic_checksum[4] = (args[1] >> 8) & 0xFF; - cic_checksum[5] = args[1] & 0xFF; + cic_region_t region = rtc_get_region(); + + uint32_t cic_config_0 = args[0] & (0x00FFFFFF); + uint32_t cic_config_1 = args[1]; + + if (region == REGION_PAL) { + cic_config_0 |= CIC_REGION; + } + if (args[0] & (1 << 24)) { + cic_config_0 |= CIC_DISABLED; + } + + fpga_reg_set(REG_CIC_0, cic_config_0); + fpga_reg_set(REG_CIC_1, cic_config_1); } void cic_set_dd_mode (bool enabled) { - cic_dd_mode = enabled; + uint32_t cic_config_0 = fpga_reg_get(REG_CIC_0); + + if (enabled) { + cic_config_0 |= CIC_64DD_MODE; + } else { + cic_config_0 &= ~(CIC_64DD_MODE); + } + + fpga_reg_set(REG_CIC_0, cic_config_0); } -void cic_hw_init (void) { + +void cic_init (void) { + while (!rtc_is_initialized()); + cic_reset_parameters(); hw_gpio_irq_setup(GPIO_ID_N64_RESET, GPIO_IRQ_FALLING, cic_irq_reset_falling); - hw_gpio_irq_setup(GPIO_ID_N64_RESET, GPIO_IRQ_RISING, cic_irq_reset_rising); - hw_gpio_irq_setup(GPIO_ID_N64_CIC_CLK, GPIO_IRQ_FALLING, cic_irq_clk_falling); - hw_gpio_irq_setup(GPIO_ID_N64_CIC_CLK, GPIO_IRQ_RISING, cic_irq_clk_rising); } -void cic_task (void) { - while (!hw_gpio_get(GPIO_ID_N64_RESET)) { - task_yield(); - } - cic_region_t region = rtc_get_region(); - if (region >= __REGION_MAX) { - region = REGION_NTSC; - rtc_set_region(region); - } +void cic_process (void) { + uint32_t cic_config_0 = fpga_reg_get(REG_CIC_0); - cic_write_id(region); + if (cic_config_0 & CIC_INVALID_REGION_DETECTED) { + cic_config_0 ^= CIC_REGION; + fpga_reg_set(REG_CIC_0, (cic_config_0 | CIC_INVALID_REGION_RESET)); - hw_tim_setup(TIM_ID_CIC, 500, cic_write_id_failed); - cic_write_seed(); - hw_tim_stop(TIM_ID_CIC); - - cic_write_checksum(); - cic_init_ram(region); - - while (1) { - uint8_t cmd = 0; - cmd |= (cic_read() << 1); - cmd |= cic_read(); - - switch (cmd) { - case 0: { - cic_compare_mode(region); - break; - } - - case 2: { - cic_x105_mode(); - break; - } - - case 3: { - cic_soft_reset(); - break; - } - - case 1: - default: { - while (1) { - task_yield(); - } - break; - } + if (cic_config_0 & CIC_REGION) { + rtc_set_region(REGION_PAL); + } else { + rtc_set_region(REGION_NTSC); } + + led_blink_error(LED_ERROR_CIC); } } diff --git a/sw/controller/src/cic.h b/sw/controller/src/cic.h index 98fe6ae..56f006e 100644 --- a/sw/controller/src/cic.h +++ b/sw/controller/src/cic.h @@ -9,8 +9,8 @@ void cic_reset_parameters (void); void cic_set_parameters (uint32_t *args); void cic_set_dd_mode (bool enabled); -void cic_hw_init (void); -void cic_task (void); +void cic_init (void); +void cic_process (void); #endif diff --git a/sw/controller/src/fpga.h b/sw/controller/src/fpga.h index f14fe76..5fafded 100644 --- a/sw/controller/src/fpga.h +++ b/sw/controller/src/fpga.h @@ -55,6 +55,8 @@ typedef enum { REG_VENDOR_DATA, REG_DEBUG_0, REG_DEBUG_1, + REG_CIC_0, + REG_CIC_1, } fpga_reg_t; @@ -186,6 +188,13 @@ typedef enum { #define DD_HEAD_TRACK_MASK (DD_HEAD_MASK | DD_TRACK_MASK) #define DD_HEAD_TRACK_INDEX_LOCK (1 << 13) +#define CIC_SEED_BIT (16) +#define CIC_REGION (1 << 24) +#define CIC_64DD_MODE (1 << 25) +#define CIC_DISABLED (1 << 26) +#define CIC_INVALID_REGION_DETECTED (1 << 27) +#define CIC_INVALID_REGION_RESET (1 << 28) + uint8_t fpga_id_get (void); uint32_t fpga_reg_get (fpga_reg_t reg); diff --git a/sw/controller/src/gvr.c b/sw/controller/src/gvr.c index 0dc6cf7..70c7909 100644 --- a/sw/controller/src/gvr.c +++ b/sw/controller/src/gvr.c @@ -1,5 +1,6 @@ #include "button.h" #include "cfg.h" +#include "cic.h" #include "dd.h" #include "flashram.h" #include "fpga.h" @@ -15,6 +16,7 @@ void gvr_task (void) { button_init(); cfg_init(); + cic_init(); dd_init(); flashram_init(); isv_init(); @@ -25,6 +27,7 @@ void gvr_task (void) { while (1) { button_process(); cfg_process(); + cic_process(); dd_process(); flashram_process(); isv_process(); diff --git a/sw/controller/src/hw.c b/sw/controller/src/hw.c index 25bdf37..0c50787 100644 --- a/sw/controller/src/hw.c +++ b/sw/controller/src/hw.c @@ -83,6 +83,7 @@ static void hw_gpio_init (gpio_id_t id, gpio_mode_t mode, gpio_ot_t ot, gpio_osp void hw_gpio_irq_setup (gpio_id_t id, gpio_irq_t irq, void (*callback)(void)) { uint8_t port = ((id >> 4) & 0x07); uint8_t pin = (id & 0x0F); + __disable_irq(); if (irq == GPIO_IRQ_FALLING) { EXTI->FTSR1 |= (EXTI_FTSR1_FT0 << pin); gpio_irq_callbacks[pin].falling = callback; @@ -92,6 +93,7 @@ void hw_gpio_irq_setup (gpio_id_t id, gpio_irq_t irq, void (*callback)(void)) { } EXTI->EXTICR[pin / 4] |= (port << (8 * (pin % 4))); EXTI->IMR1 |= (EXTI_IMR1_IM0 << pin); + __enable_irq(); } uint32_t hw_gpio_get (gpio_id_t id) { @@ -509,7 +511,7 @@ static void hw_init_crc (void) { static void hw_init_misc (void) { hw_gpio_init(GPIO_ID_N64_RESET, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_DOWN, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_N64_CIC_CLK, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0); - hw_gpio_init(GPIO_ID_N64_CIC_DQ, GPIO_OUTPUT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 1); + hw_gpio_init(GPIO_ID_N64_CIC_DQ, GPIO_INPUT, GPIO_OD, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 1); hw_gpio_init(GPIO_ID_FPGA_INT, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0); hw_gpio_init(GPIO_ID_RTC_MFP, GPIO_INPUT, GPIO_PP, GPIO_SPEED_VLOW, GPIO_PULL_UP, GPIO_AF_0, 0); } @@ -528,14 +530,14 @@ void hw_init (void) { hw_init_misc(); NVIC_SetPriority(EXTI0_1_IRQn, 0); - NVIC_SetPriority(EXTI2_3_IRQn, 1); - NVIC_SetPriority(EXTI4_15_IRQn, 2); - NVIC_SetPriority(I2C1_IRQn, 1); + NVIC_SetPriority(EXTI2_3_IRQn, 0); + NVIC_SetPriority(EXTI4_15_IRQn, 0); + NVIC_SetPriority(I2C1_IRQn, 0); NVIC_SetPriority(TIM14_IRQn, 0); - NVIC_SetPriority(TIM16_IRQn, 1); - NVIC_SetPriority(TIM17_IRQn, 2); - NVIC_SetPriority(TIM3_IRQn, 2); - NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 1); + NVIC_SetPriority(TIM16_IRQn, 0); + NVIC_SetPriority(TIM17_IRQn, 0); + NVIC_SetPriority(TIM3_IRQn, 0); + NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 0); NVIC_EnableIRQ(EXTI0_1_IRQn); NVIC_EnableIRQ(EXTI2_3_IRQn); diff --git a/sw/controller/src/rtc.c b/sw/controller/src/rtc.c index 5ffd443..99e251b 100644 --- a/sw/controller/src/rtc.c +++ b/sw/controller/src/rtc.c @@ -48,6 +48,7 @@ static rtc_settings_t rtc_settings = { .led_enabled = true, }; static volatile bool rtc_settings_pending = false; +static volatile bool rtc_initialized = false; static const uint8_t rtc_regs_bit_mask[7] = { 0b01111111, @@ -199,9 +200,15 @@ static void rtc_init (void) { rtc_write(RTC_ADDRESS_SRAM_VERSION, (uint8_t *) (&settings_version), 4); rtc_write_settings(); } + + rtc_initialized = true; } +bool rtc_is_initialized (void) { + return rtc_initialized; +} + bool rtc_get_time (rtc_time_t *time) { bool vaild; diff --git a/sw/controller/src/rtc.h b/sw/controller/src/rtc.h index 06c70cf..be7dc8a 100644 --- a/sw/controller/src/rtc.h +++ b/sw/controller/src/rtc.h @@ -21,6 +21,7 @@ typedef struct { } rtc_settings_t; +bool rtc_is_initialized (void); bool rtc_get_time (rtc_time_t *time); void rtc_set_time (rtc_time_t *time); uint8_t rtc_get_region (void); diff --git a/sw/controller/src/task.c b/sw/controller/src/task.c index b2ddac5..8240aa4 100644 --- a/sw/controller/src/task.c +++ b/sw/controller/src/task.c @@ -1,3 +1,4 @@ +#include #include #include #include "task.h" @@ -8,18 +9,9 @@ #define TASK_STACK_FILL_VALUE (0xDEADBEEF) -typedef enum { - TASK_FLAG_NONE = 0, - TASK_FLAG_READY = (1 << 0), - TASK_FLAG_RESET = (1 << 1), -} task_flags_t; - - typedef struct { - uint32_t initial_pc; - uint32_t initial_sp; - uint32_t sp; - task_flags_t flags; + volatile uint32_t sp; + volatile bool ready; } task_t; @@ -28,42 +20,21 @@ static volatile task_id_t task_current = 0; static void task_exit (void) { - task_table[task_current].flags = TASK_FLAG_NONE; - task_yield(); - while (1); -} - -static void task_initialize (task_id_t id) { - task_t *task = &task_table[id]; - uint32_t *sp = ((uint32_t *) (task->initial_sp)); - *--sp = TASK_INITIAL_XPSR; - *--sp = task->initial_pc; - *--sp = ((uint32_t) (task_exit)); - for (int i = 0; i < 13; i++) { - *--sp = 0; + while (1) { + task_yield(); } - task->sp = ((uint32_t) (sp)); -} - -static void task_reset (task_id_t id) { - task_table[id].flags &= ~(TASK_FLAG_RESET); - task_initialize(id); } static uint32_t task_switch_context (uint32_t sp) { task_table[task_current].sp = sp; for (task_id_t id = 0; id < __TASK_ID_MAX; id++) { - if (task_table[id].flags & TASK_FLAG_READY) { + if (task_table[id].ready) { task_current = id; break; } } - if (task_table[task_current].flags & TASK_FLAG_RESET) { - task_reset(task_current); - } - return task_table[task_current].sp; } @@ -73,26 +44,30 @@ void task_create (task_id_t id, void (*code)(void), void *stack, size_t stack_si for (size_t i = 0; i < stack_size; i += sizeof(uint32_t)) { (*(uint32_t *) (stack + i)) = TASK_STACK_FILL_VALUE; } + uint32_t *sp = ((uint32_t *) ((uint32_t) (stack) + stack_size)); + *--sp = TASK_INITIAL_XPSR; + *--sp = (uint32_t) (code); + *--sp = ((uint32_t) (task_exit)); + for (int i = 0; i < 13; i++) { + *--sp = 0; + } task_t *task = &task_table[id]; - task->initial_pc = (uint32_t) (code); - task->initial_sp = (((uint32_t) (stack)) + stack_size); - task->flags = TASK_FLAG_READY; - task_initialize(id); + task->sp = ((uint32_t) (sp)); + task->ready = true; } } void task_yield (void) { - task_table[task_current].flags &= ~(TASK_FLAG_READY); + __disable_irq(); + task_table[task_current].ready = false; + __enable_irq(); TASK_CONTEXT_SWITCH(); } void task_set_ready (task_id_t id) { - task_table[id].flags |= TASK_FLAG_READY; - TASK_CONTEXT_SWITCH(); -} - -void task_set_ready_and_reset (task_id_t id) { - task_table[id].flags |= (TASK_FLAG_RESET | TASK_FLAG_READY); + __disable_irq(); + task_table[id].ready = true; + __enable_irq(); TASK_CONTEXT_SWITCH(); } diff --git a/sw/controller/src/task.h b/sw/controller/src/task.h index 684f5d7..71ad4e3 100644 --- a/sw/controller/src/task.h +++ b/sw/controller/src/task.h @@ -6,7 +6,6 @@ typedef enum { - TASK_ID_CIC, TASK_ID_RTC, TASK_ID_LED, TASK_ID_GVR, @@ -17,7 +16,6 @@ typedef enum { void task_create (task_id_t id, void (*code)(void), void *stack, size_t stack_size); void task_yield (void); void task_set_ready (task_id_t id); -void task_set_ready_and_reset (task_id_t id); size_t task_get_stack_usage (void *stack, size_t stack_size); void task_scheduler_start (void); diff --git a/sw/deployer/src/sc64/types.rs b/sw/deployer/src/sc64/types.rs index 0164d61..505db68 100644 --- a/sw/deployer/src/sc64/types.rs +++ b/sw/deployer/src/sc64/types.rs @@ -852,9 +852,12 @@ impl TryFrom> for McuStackUsage { impl Display for McuStackUsage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.cic > 0 { + f.write_fmt(format_args!("CIC: {}, ", self.cic))?; + } f.write_fmt(format_args!( - "CIC: {}, RTC: {}, LED: {}, GVR: {}", - self.cic, self.rtc, self.led, self.gvr + "RTC: {}, LED: {}, GVR: {}", + self.rtc, self.led, self.gvr )) } }