added azadi_soc design files
diff --git a/verilog/rtl/.azadi_soc_top.sv.swp b/verilog/rtl/.azadi_soc_top.sv.swp new file mode 100644 index 0000000..cb81277 --- /dev/null +++ b/verilog/rtl/.azadi_soc_top.sv.swp Binary files differ
diff --git a/verilog/rtl/.user_project_wrapper.v.swp b/verilog/rtl/.user_project_wrapper.v.swp new file mode 100644 index 0000000..ca62df7 --- /dev/null +++ b/verilog/rtl/.user_project_wrapper.v.swp Binary files differ
diff --git a/verilog/rtl/PWM.v b/verilog/rtl/PWM.v new file mode 100644 index 0000000..44078a0 --- /dev/null +++ b/verilog/rtl/PWM.v
@@ -0,0 +1,251 @@ +/* +control register [7:0]ctrl: +bit 0: When set, external clock is chosen for PWM/timer. When cleared, wb clock is used for PWM/timer. +bit 1: When set, PWM is enabled. When cleared, timer is enabled. +bit 2: When set, PWM/timer starts. When cleared, PWM/timer stops. +bit 3: When set, timer runs continuously. When cleared, timer runs one time. +bit 4: When set, o_pwm enabled. +bit 5: timer interrupt bit When it is written with 0, interrupt request is cleared. +bit 6: When set, a 16-bit external signal i_DC is used as duty cycle. When cleared, register DC is used. +bit 7: When set, counter reset for PWM/timer, it's output and bit 5 will also be cleared. When changing from PWM mode to timer mode reset is needed before timer starts. +*/ +module PWM( +//tlul interface + input clk_i, + input rst_ni, + + input re_i, + input we_i, + input [7:0] addr_i, + input [31:0] wdata_i, + input [3:0] be_i, + output [31:0] rdata_o, + //output error_o, + + input i_extclk, + input [15:0] i_DC, + input i_valid_DC, + output o_pwm, + output o_pwm_2, + output reg oe_pwm1, + output reg oe_pwm2 + +); + +////////////////////control logic//////////////////////////// +parameter adr_ctrl_1 = 0, + adr_divisor_1= 4, + adr_period_1 = 8, + adr_DC_1 = 12; + +parameter adr_ctrl_2 = 16, + adr_divisor_2= 20, + adr_period_2 = 24, + adr_DC_2 = 28; + + + + +reg [7:0] ctrl; +reg [15:0] period; +reg [15:0] DC; +reg [15:0] divisor; + +reg [7:0] ctrl_2; +reg [15:0] period_2; +reg [15:0] DC_2; +reg [15:0] divisor_2; + +wire write; + +assign write = we_i & ~re_i; + +always@(posedge clk_i) + if(~rst_ni)begin + ctrl[4:2] <= 0; + ctrl[0] <= 0; + ctrl[7:6] <= 0; + DC <= 0; + period <= 0; + divisor <= 0; + + + ctrl_2[4:2] <= 0; + ctrl_2[0] <= 0; + ctrl_2[7:6] <= 0; + DC_2 <= 0; + period_2 <= 0; + divisor_2 <= 0; + end + else if(write)begin + case(addr_i) + adr_ctrl_1:begin + ctrl[0] <= wdata_i[0]; + ctrl[4:2] <= wdata_i[4:2]; + ctrl[7:6] <= wdata_i[7:6]; + end + + adr_ctrl_2:begin + ctrl_2[0] <= wdata_i[0]; + ctrl_2[4:2] <= wdata_i[4:2]; + ctrl_2[7:6] <= wdata_i[7:6]; + end + + adr_divisor_1 : divisor <= wdata_i[15:0]; + adr_period_1 : period <= wdata_i[15:0]; + adr_DC_1 : DC <= wdata_i[15:0]; + + adr_divisor_2 : divisor_2 <= wdata_i[15:0]; + adr_period_2 : period_2 <= wdata_i[15:0]; + adr_DC_2 : DC_2 <= wdata_i[15:0]; + endcase + end + + + +wire pwm; +always @(posedge clk_i) begin + ctrl[1] <= 1'b1; +end + + +assign pwm = ctrl[1]; + +wire pwm_1; +always @(posedge clk_i) begin + ctrl_2[1] <= 1'b1; +end + +assign pwm_1 = ctrl_2[1]; +wire eclk_2,oclk_2; +/////////////////////////////////////////////////////////// + +//////down clocking for pwm/////////////////// +wire clk_source; +wire eclk,oclk; +assign clk_source = clk_i; +down_clocking_even clock_div_ev( + .clk_i (clk_source) , + .rst_ni (rst_ni), + .i_divisor ({1'b0,divisor[15:1]}), + .o_clk (eclk) +); +down_clocking_odd clock_div_od( + .clk_i (clk_source), + .rst_ni (rst_ni), + .i_divisor ({1'b0,divisor[15:1]}), + .o_clk (oclk) +); +wire clk; +assign clk = divisor[0]? oclk: eclk; + + + + +down_clocking_even clock_div_ev_2( + .clk_i (clk_source) , + .rst_ni (rst_ni), + .i_divisor ({1'b0,divisor_2[15:1]}), + .o_clk (eclk_2) +); +down_clocking_odd clock_div_od_2( + .clk_i (clk_source), + .rst_ni (rst_ni), + .i_divisor ({1'b0,divisor_2[15:1]}), + .o_clk (oclk_2) +); +wire clk_2; +assign clk_2 = divisor_2[0]? oclk_2: eclk_2; + +/////////////////////////////////////////////////////// + +/////////////////main counter ////////////////////////// +reg [15:0] ct; +reg pts; //PWM signal +reg [15:0] extDC; +wire [15:0] DC_1; +assign DC_1 = ctrl[6]? extDC: DC; //external or internal duty cycle toggle +wire [15:0] period_1; +assign period_1 = (period==0)? 0: (period-1); + +wire rst_ct; +assign rst_ct = ~rst_ni|ctrl[7]; + + +reg [15:0] ct_2; +reg pts_2; //PWM signal +reg [15:0] extDC_2; +wire [15:0] DCw_2; +assign DCw_2 = ctrl_2[6]? extDC_2: DC_2; //external or internal duty cycle toggle +wire [15:0] period_P2; +assign period_P2 = (period_2==0)? 0: (period_2-1); + +wire rst_ct_2; +assign rst_ct_2 = ~rst_ni|ctrl_2[7]; + +always@(posedge clk ) + if(rst_ct)begin + pts <= 0; + ct <= 0; + extDC <= 0; + end + else begin + if(i_valid_DC) extDC <= i_DC; + if(ctrl[2])begin + if(pwm) begin + oe_pwm1 <= 1'b1; + if(ct >= period_1) ct <= 0; + else ct <= ct+1; + + if(ct < DC_1) pts <= 1'b1; + else pts <= 1'b0; + end + end + else begin + pts <= 1'b0; + ct <= 0; + oe_pwm1 <= 0; + end +end + + + +always@(posedge clk_2 ) + if(rst_ct_2)begin + pts_2 <= 0; + ct_2 <= 0; + extDC_2 <= 0; + end + else begin + if(i_valid_DC) extDC_2 <= i_DC; + if(ctrl_2[2])begin + if(pwm_1) begin + oe_pwm2 <= 1'b1; + if(ct_2 >= period_P2) ct_2 <= 0; + else ct_2 <= ct_2+1; + + if(ct_2 < DCw_2) pts_2 <= 1'b1; + else pts_2 <= 1'b0; + end + end + else begin + pts_2 <= 1'b0; + ct_2 <= 0; + oe_pwm2 <= 1'b0; + end +end +////////////////////////////////////////////////////////// + +assign o_pwm = ctrl[4]? pts: 0; +assign o_pwm_2 = ctrl_2[4]? pts_2: 0; +assign rdata_o = (addr_i == adr_ctrl_1) ? {8'h0,ctrl} : + (addr_i == adr_divisor_1)? divisor : + (addr_i == adr_period_1) ? period : + (addr_i == adr_DC_1) ? DC : + (addr_i == adr_DC_2) ? DC_2 : + (addr_i == adr_period_2) ? period_2 : + (addr_i == adr_divisor_2)? divisor_2 : + (addr_i == adr_ctrl_2) ? {8'h0,ctrl_2}:0; + + +endmodule
diff --git a/verilog/rtl/azadi_soc_top.sv b/verilog/rtl/azadi_soc_top.sv new file mode 100644 index 0000000..da32e03 --- /dev/null +++ b/verilog/rtl/azadi_soc_top.sv
@@ -0,0 +1,520 @@ + +module azadi_soc_top ( + input clk_i, + input rst_ni, + input prog, + //output system_rst_ni, + //output prog_rst_ni, + input logic [31:0] gpio_i, + output logic [31:0] gpio_o, + output logic [31:0] gpio_oe, + + // jtag interface + input logic jtag_tck_i, + input logic jtag_tms_i, + input logic jtag_trst_ni, + input logic jtag_tdi_i, + output logic jtag_tdo_o, + output logic jtag_tdo_oe_o, + + // uart-periph interface + output logic uart_tx, + output logic tx_en_o, + input logic uart_rx, + + // PWM interface + + output logic pwm_o, + output logic pwm_o_2, + output logic pwm1_oe, + output logic pwm2_oe, + + // SPI interface + + output logic [`SPI_SS_NB-1:0] ss_o, + output logic sclk_o, + output logic sd_o, + output logic sd_oe, + input logic sd_i +); + +localparam logic [31:0] JTAG_ID = { + 4'h0, // Version + 16'h4F54, // Part Number: "OT" + 11'h426, // Manufacturer Identity: Google + 1'b1 // (fixed) +}; + + + logic prog_rst_n; + logic system_rst_ni; + logic [31:0] gpio_in; + logic [31:0] gpio_out; + + assign gpio_in = gpio_i; + assign gpio_o = gpio_out; + + logic instr_valid; + logic [11:0] tlul_addr; + logic req_i; + logic [31:0] tlul_data; + logic dbg_req; + logic dbg_rst; + + + // instruction sram interface + logic instr_csb; + logic [11:0] instr_addr; + logic [31:0] instr_wdata; + logic [3:0] instr_wmask; + logic instr_we; + logic [31:0] instr_rdata; + + // data sram interface + logic data_csb; + logic [11:0] data_addr; + logic [31:0] data_wdata; + logic [3:0] data_wmask; + logic data_we; + logic [31:0] data_rdata; + + logic [31:0] iccm_ctrl_data; + logic iccm_ctrl_we; + logic [11:0] iccm_ctrl_addr_o; + + + tlul_pkg::tl_h2d_t ifu_to_xbar; + tlul_pkg::tl_d2h_t xbar_to_ifu; + tlul_pkg::tl_h2d_t xbar_to_iccm; + tlul_pkg::tl_d2h_t iccm_to_xbar; + + tlul_pkg::tl_h2d_t lsu_to_xbar; + tlul_pkg::tl_d2h_t xbar_to_lsu; + + tlul_pkg::tl_h2d_t xbar_to_dccm; + tlul_pkg::tl_d2h_t dccm_to_xbar; + + tlul_pkg::tl_h2d_t xbarp_to_gpio; + tlul_pkg::tl_d2h_t gpio_to_xbarp; + + tlul_pkg::tl_h2d_t dm_to_xbar; + tlul_pkg::tl_d2h_t xbar_to_dm; + + tlul_pkg::tl_h2d_t dbgrom_to_xbar; + tlul_pkg::tl_d2h_t xbar_to_dbgrom; + + tlul_pkg::tl_h2d_t plic_req; + tlul_pkg::tl_d2h_t plic_resp; + + tlul_pkg::tl_h2d_t xbar_to_uart; + tlul_pkg::tl_d2h_t uart_to_xbar; + + tlul_pkg::tl_h2d_t xbar_to_timer; + tlul_pkg::tl_d2h_t timer_to_xbar; + + tlul_pkg::tl_h2d_t xbar_to_pwm; + tlul_pkg::tl_d2h_t pwm_to_xbar; + + tlul_pkg::tl_h2d_t xbar_to_spi; + tlul_pkg::tl_d2h_t spi_to_xbar; + + // interrupt vector + logic [43:0] intr_vector; + + // Interrupt source list + logic [31:0] intr_gpio; + logic intr_uart0_tx_watermark; + logic intr_uart0_rx_watermark; + logic intr_uart0_tx_empty; + logic intr_uart0_rx_overflow; + logic intr_uart0_rx_frame_err; + logic intr_uart0_rx_break_err; + logic intr_uart0_rx_timeout; + logic intr_uart0_rx_parity_err; + logic intr_req; + logic intr_srx; + logic intr_stx; + logic intr_timer; + + assign intr_vector = { + intr_srx, + intr_stx, + intr_uart0_rx_parity_err, + intr_uart0_rx_timeout, + intr_uart0_rx_break_err, + intr_uart0_rx_frame_err, + intr_uart0_rx_overflow, + intr_uart0_tx_empty, + intr_uart0_rx_watermark, + intr_uart0_tx_watermark, + intr_gpio, + 1'b0 + }; + +// jtag interface + + jtag_pkg::jtag_req_t jtag_req; + jtag_pkg::jtag_rsp_t jtag_rsp; + + + assign jtag_req.tck = jtag_tck_i; + assign jtag_req.tms = jtag_tms_i; + assign jtag_req.trst_n = jtag_trst_ni; + assign jtag_req.tdi = jtag_tdi_i; + assign jtag_tdo_o = jtag_rsp.tdo; + assign jtag_tdo_oe_o = jtag_rsp.tdo_oe; + + +brq_core_top #( + .PMPEnable (1'b0), + .PMPGranularity (0), + .PMPNumRegions (4), + .MHPMCounterNum (0), + .MHPMCounterWidth (40), + .RV32E (1'b0), + .RV32M (brq_pkg::RV32MSlow), + .RV32B (brq_pkg::RV32BNone), + .RegFile (brq_pkg::RegFileFF), + .BranchTargetALU (1'b0), + .WritebackStage (1'b1), + .ICache (1'b0), + .ICacheECC (1'b0), + .BranchPredictor (1'b0), + .DbgTriggerEn (1'b1), + .DbgHwBreakNum (1), + .Securebrq (1'b0), + .DmHaltAddr (tl_main_pkg::ADDR_SPACE_DEBUG_ROM + 32'h 800), + .DmExceptionAddr (tl_main_pkg::ADDR_SPACE_DEBUG_ROM + dm::ExceptionAddress) +) u_top ( + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + // instruction memory interface + .tl_i_i (xbar_to_ifu), + .tl_i_o (ifu_to_xbar), + + // data memory interface + .tl_d_i (xbar_to_lsu), + .tl_d_o (lsu_to_xbar), + + .test_en_i (1'b0), // enable all clk_i gates for testing + + .hart_id_i (32'b0), + .boot_addr_i (32'h20000000), + + // Interrupt inputs + .irq_software_i (1'b0), + .irq_timer_i (intr_timer), + .irq_external_i (intr_req), + .irq_fast_i ('0), + .irq_nm_i (1'b0), // non-maskeable interrupt + + // Debug Interface + .debug_req_i (dbg_req), + // CPU Control Signals + .fetch_enable_i (1'b1), + .alert_minor_o (), + .alert_major_o (), + .core_sleep_o () +); + +// Debug module +rv_dm #( + .NrHarts(1), + .IdcodeValue(JTAG_ID) + // .DirectDmiTap (DirectDmiTap) +) debug_module ( + .clk_i(clk_i), // clk_i + .rst_ni(rst_ni), // asynchronous reset active low, connect PoR + // here, not the system reset + .testmode_i('0), + .ndmreset_o(dbg_rst), // non-debug module reset + .dmactive_o(), // debug module is active + .debug_req_o(dbg_req), // async debug request + .unavailable_i(1'b0), // communicate whether the hart is unavailable + // (e.g.: power down) + + // bus device with debug memory, for an execution based technique + .tl_d_i(dbgrom_to_xbar), + .tl_d_o(xbar_to_dbgrom), + + // bus host, for system bus accesses + .tl_h_o(dm_to_xbar), + .tl_h_i(xbar_to_dm), + + .jtag_req_i(jtag_req), + .jtag_rsp_o(jtag_rsp) +); + + + +// main xbar module + tl_xbar_main main_swith ( + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + // Host interfaces + .tl_brqif_i (ifu_to_xbar), + .tl_brqif_o (xbar_to_ifu), + .tl_brqlsu_i (lsu_to_xbar), + .tl_brqlsu_o (xbar_to_lsu), + .tl_dm_sba_i (dm_to_xbar), + .tl_dm_sba_o (xbar_to_dm), + + // Device interfaces + .tl_iccm_o (xbar_to_iccm), + .tl_iccm_i (iccm_to_xbar), + .tl_debug_rom_o (dbgrom_to_xbar), + .tl_debug_rom_i (xbar_to_dbgrom), + .tl_dccm_o (xbar_to_dccm), + .tl_dccm_i (dccm_to_xbar), + .tl_timer0_o (xbar_to_timer), + .tl_timer0_i (timer_to_xbar), + .tl_uart_o (xbar_to_uart), + .tl_uart_i (uart_to_xbar), + .tl_spi_o (xbar_to_spi), + .tl_spi_i (spi_to_xbar), + .tl_pwm_o (xbar_to_pwm), + .tl_pwm_i (pwm_to_xbar), + .tl_gpio_o (xbarp_to_gpio), + .tl_gpio_i (gpio_to_xbarp), + .tl_plic_o (plic_req), + .tl_plic_i (plic_resp) +); + + +// timer +rv_timer timer0( + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + .tl_i (xbar_to_timer), + .tl_o (timer_to_xbar), + + .intr_timer_expired_0_0_o (intr_timer) +); + +// PWM module + +pwm_top u_pwm( + + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + .tl_i (xbar_to_pwm), + .tl_o (pwm_to_xbar), + + + .pwm_o (pwm_o), + .pwm_o_2 (pwm_o_2), + .pwm1_oe (pwm1_oe), + .pwm2_oe (pwm2_oe) +); + + +// spi module + +spi_top u_spi_host( + + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + .tl_i (xbar_to_spi), + .tl_o (spi_to_xbar), + + // SPI signals + .intr_rx_o (intr_srx), + .intr_tx_o (intr_stx), + .ss_o (ss_o), + .sclk_o (sclk_o), + .sd_o (sd_o), + .sd_oe (sd_oe), + .sd_i (sd_i) +); + + +//GPIO module +gpio GPIO ( + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + // Below Regster interface can be changed + .tl_i (xbarp_to_gpio), + .tl_o (gpio_to_xbarp), + + .cio_gpio_i (gpio_in), + .cio_gpio_o (gpio_out), + .cio_gpio_en_o (gpio_oe), + + .intr_gpio_o (intr_gpio ) +); + + +rstmgr reset_manager( + .clk_i(clk_i), + .rst_ni(rst_ni), + .ndmreset (dbg_rst), + .prog_rst_ni(prog_rst_ni), + .sys_rst_ni(system_rst_ni) +); + +rv_plic intr_controller ( + .clk_i(clk_i), + .rst_ni(system_rst_ni), + + // Bus Interface (device) + .tl_i (plic_req), + .tl_o (plic_resp), + + // Interrupt Sources + .intr_src_i (intr_vector), + + // Interrupt notification to targets + .irq_o (intr_req), + .msip_o() +); + +uart u_uart0( + .clk_i (clk_i ), + .rst_ni (system_rst_ni ), + + // Bus Interface + .tl_i (xbar_to_uart ), + .tl_o (uart_to_xbar ), + + // Generic IO + .cio_rx_i (uart_rx ), + .cio_tx_o (uart_tx ), + .cio_tx_en_o (tx_en_o ), + + // Interrupts + .intr_tx_watermark_o (intr_uart0_tx_watermark ), + .intr_rx_watermark_o (intr_uart0_rx_watermark ), + .intr_tx_empty_o (intr_uart0_tx_empty ), + .intr_rx_overflow_o (intr_uart0_rx_overflow ), + .intr_rx_frame_err_o (intr_uart0_rx_frame_err ), + .intr_rx_break_err_o (intr_uart0_rx_break_err ), + .intr_rx_timeout_o (intr_uart0_rx_timeout ), + .intr_rx_parity_err_o (intr_uart0_rx_parity_err) +); + +logic rx_dv_i; +logic [7:0] rx_byte_i; + +iccm_controller u_dut( + .clk_i (~clk_i), + .rst_ni (rst_ni), + .prog_i (prog), + .rx_dv_i (rx_dv_i), + .rx_byte_i (rx_byte_i), + .we_o (iccm_ctrl_we), + .addr_o (iccm_ctrl_addr_o), + .wdata_o (iccm_ctrl_data), + .reset_o (prog_rst_ni) +); + +uart_rx_prog u_uart_rx_prog( + .clk_i (~clk_i), + .rst_ni (rst_ni), + .i_Rx_Serial (uart_rx), + .CLKS_PER_BIT (16'd1667), + .o_Rx_DV (rx_dv_i), + .o_Rx_Byte (rx_byte_i) +); + + +// dummy instruction memory +instr_mem_top iccm_adapter( + .clk_i (clk_i), + .rst_ni (system_rst_ni), + + .tl_i (xbar_to_iccm), + .tl_o (iccm_to_xbar), +// iccm controller interface + .iccm_ctrl_addr (iccm_ctrl_addr_o), + .iccm_ctrl_wdata (iccm_ctrl_data), + .iccm_ctrl_we (iccm_ctrl_we), + .prog_rst_ni (prog_rst_ni), + + +// instruction sram interface + .csb (instr_csb), + .addr_o (instr_addr), + .wdata_o (instr_wdata), + .wmask_o (instr_wmask), + .we_o (instr_we), + .rdata_i (instr_rdata) +); + + + sram #( + .NUM_WMASKS (4), +// .MEMD (4096), + .DATA_WIDTH (32), // data width +// .nRPORTS (1) , // number of reading ports +// .nWPORTS (1), // number of write ports + .IZERO (0) , // binary / Initial RAM with zeros (has priority over IFILE) +// .BASIC_MODEL (1024), + .ADDR_WIDTH (11) + ) u_iccm ( /*`ifdef USE_POWER_PINS + inout vdd; + inout gnd; + `endif*/ + .clk0 (~clk_i), // clock + .csb0 (instr_csb), // active low chip select + .web0 (instr_we), // active low write control + .wmask0 (instr_wmask), // write mask + .addr0 (instr_addr[10:0]), + .din0 (instr_wdata), + .dout0 (instr_rdata), + .clk1 (1'b0), + .csb1 (1'b1), + .addr1 ('0), + .dout1 () + ); +// dummy data memory + +data_mem_top dccm_adapter( + .clk_i (clk_i), + .rst_ni (system_rst_ni), + +// tl-ul insterface + .tl_d_i (xbar_to_dccm), + .tl_d_o (dccm_to_xbar), + + // sram interface + .csb (data_csb), + .addr_o (data_addr), + .wdata_o (data_wdata), + .wmask_o (data_wmask), + .we_o (data_we), + .rdata_i (data_rdata) +); + + +sram #( + .NUM_WMASKS (4), +// .MEMD (4096), + .DATA_WIDTH (32), // data width +// .nRPORTS (1) , // number of reading ports +// .nWPORTS (1), // number of write ports + .IZERO (0) , // binary / Initial RAM with zeros (has priority over IFILE) +// .BASIC_MODEL (1024), + .ADDR_WIDTH (11) + ) u_dccm ( /*`ifdef USE_POWER_PINS + inout vdd; + inout gnd; +`endif*/ + .clk0 (~clk_i), // clock + .csb0 (data_csb), // active low chip select + .web0 (data_we), // active low write control + .wmask0 (data_wmask), // write mask + .addr0 (data_addr[10:0]), + .din0 (data_wdata), + .dout0 (data_rdata), + .clk1 (1'b0), + .csb1 (1'b1), + .addr1 ('0), + .dout1 () + ); +endmodule
diff --git a/verilog/rtl/brq_core.sv b/verilog/rtl/brq_core.sv new file mode 100644 index 0000000..1e83243 --- /dev/null +++ b/verilog/rtl/brq_core.sv
@@ -0,0 +1,1592 @@ +`ifdef RISCV_FORMAL + `define RVFI +`endif + + +/** + * Top level module of the brq RISC-V core + */ +module brq_core #( + parameter bit PMPEnable = 1'b0, + parameter int unsigned PMPGranularity = 0, + parameter int unsigned PMPNumRegions = 0, + parameter int unsigned MHPMCounterNum = 0, + parameter int unsigned MHPMCounterWidth = 40, + parameter bit RV32E = 1'b0, + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, + parameter brq_pkg::rv32b_e RV32B = brq_pkg::RV32BNone, + parameter brq_pkg::regfile_e RegFile = brq_pkg::RegFileFF, + parameter brq_pkg::rvfloat_e RVF = brq_pkg::RV32FSingle, // for floating point + parameter int unsigned FloatingPoint = 1'b1, + parameter bit BranchTargetALU = 1'b0, + parameter bit WritebackStage = 1'b1, + parameter bit ICache = 1'b0, + parameter bit ICacheECC = 1'b0, + parameter bit BranchPredictor = 1'b0, + parameter bit DbgTriggerEn = 1'b0, + parameter int unsigned DbgHwBreakNum = 1, + parameter bit Securebrq = 1'b0, + parameter int unsigned DmHaltAddr = 32'h1A110800, + parameter int unsigned DmExceptionAddr = 32'h1A110808 +) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + input logic test_en_i, // enable all clock gates for testing + + input logic [31:0] hart_id_i, + input logic [31:0] boot_addr_i, + + // Instruction memory interface + output logic instr_req_o, + input logic instr_gnt_i, + input logic instr_rvalid_i, + output logic [31:0] instr_addr_o, + input logic [31:0] instr_rdata_i, + input logic instr_err_i, + + // Data memory interface + output logic data_req_o, + input logic data_gnt_i, + input logic data_rvalid_i, + output logic data_we_o, + output logic [3:0] data_be_o, + output logic [31:0] data_addr_o, + output logic [31:0] data_wdata_o, + input logic [31:0] data_rdata_i, + input logic data_err_i, + + // Interrupt inputs + input logic irq_software_i, + input logic irq_timer_i, + input logic irq_external_i, + input logic [14:0] irq_fast_i, + input logic irq_nm_i, // non-maskeable interrupt + + // Debug Interface + input logic debug_req_i, + + // RISC-V Formal Interface + // Does not comply with the coding standards of _i/_o suffixes, but follows + // the convention of RISC-V Formal Interface Specification. +`ifdef RVFI + output logic rvfi_valid, + output logic [63:0] rvfi_order, + output logic [31:0] rvfi_insn, + output logic rvfi_trap, + output logic rvfi_halt, + output logic rvfi_intr, + output logic [ 1:0] rvfi_mode, + output logic [ 1:0] rvfi_ixl, + output logic [ 4:0] rvfi_rs1_addr, + output logic [ 4:0] rvfi_rs2_addr, + output logic [ 4:0] rvfi_rs3_addr, + output logic [31:0] rvfi_rs1_rdata, + output logic [31:0] rvfi_rs2_rdata, + output logic [31:0] rvfi_rs3_rdata, + output logic [ 4:0] rvfi_rd_addr, + output logic [31:0] rvfi_rd_wdata, + output logic [31:0] rvfi_pc_rdata, + output logic [31:0] rvfi_pc_wdata, + output logic [31:0] rvfi_mem_addr, + output logic [ 3:0] rvfi_mem_rmask, + output logic [ 3:0] rvfi_mem_wmask, + output logic [31:0] rvfi_mem_rdata, + output logic [31:0] rvfi_mem_wdata, +`endif + + // CPU Control Signals + input logic fetch_enable_i, + output logic alert_minor_o, + output logic alert_major_o, + output logic core_sleep_o +); + + import brq_pkg::*; + + // floating point + localparam int unsigned W = 32; + logic fp_flush; + logic in_ready_c2fpu; // ready - from core to FPU + logic in_valid_c2fpu; // valid - from FPU to core + logic out_ready_fpu2c; // ready - from FPU to core + logic out_valid_fpu2c; // valid - from core to FPU + logic valid_id_fpu; // select which valid signal will go to dec + logic fp_rm_dynamic; + logic fp_alu_op_mod; + logic [4:0] fp_rf_raddr_a; + logic [4:0] fp_rf_raddr_b; + logic [4:0] fp_rf_raddr_c; + logic [W-1:0] fp_rf_rdata_a; + logic [W-1:0] fp_rf_rdata_b; + logic [W-1:0] fp_rf_rdata_c; + logic fp_rf_wen_id; + logic is_fp_instr; + logic [2:0][W-1:0] fp_operands; // three operands in fpu + logic fp_busy; + logic fpu_busy_idu; + logic [W-1:0] fp_result; + logic [ 31:0] data_wb; + logic [4:0] fp_rf_waddr_id; + logic [4:0] fp_rf_waddr_wb; + logic fp_rf_we; + logic fp_rf_wen_wb; + logic use_fp_rs1; + logic use_fp_rs2; + logic use_fp_rd; + logic fp_rf_write_wb; + logic [31:0] rf_int_fp_lsu; + logic fp_swap_oprnds; + logic fpu_is_busy; + logic fp_load; + logic [31:0] fp_rf_wdata_wb; + fpnew_pkg::status_t fp_status; + fpnew_pkg::operation_e fp_operation; + fpnew_pkg::roundmode_e fp_rounding_mode; + fpnew_pkg::roundmode_e fp_frm_csr; + fpnew_pkg::roundmode_e fp_frm_fpnew; + fpnew_pkg::operation_e fp_alu_operator; + fpnew_pkg::fp_format_e fp_src_fmt; + fpnew_pkg::fp_format_e fp_dst_fmt; + + // brq + localparam int unsigned PMP_NUM_CHAN = 2; + localparam bit DataIndTiming = Securebrq; + localparam bit DummyInstructions = Securebrq; + localparam bit PCIncrCheck = Securebrq; + localparam bit ShadowCSR = Securebrq; + // Speculative branch option, trades-off performance against timing. + // Setting this to 1 eases branch target critical paths significantly but reduces performance + // by ~3% (based on CoreMark/MHz score). + // Set by default in the max PMP config which has the tightest budget for branch target timing. + localparam bit SpecBranch = PMPEnable & (PMPNumRegions == 16); + localparam bit RegFileECC = Securebrq; + localparam int unsigned RegFileDataWidth = RegFileECC ? 32 + 7 : 32; + + // IF/ID signals + logic dummy_instr_id; + logic instr_valid_id; + logic instr_new_id; + logic [31:0] instr_rdata_id; // Instruction sampled inside IF stage + logic [31:0] instr_rdata_alu_id; // Instruction sampled inside IF stage (replicated to + // ease fan-out) + logic [15:0] instr_rdata_c_id; // Compressed instruction sampled inside IF stage + logic instr_is_compressed_id; + logic instr_perf_count_id; + logic instr_bp_taken_id; + logic instr_fetch_err; // Bus error on instr fetch + logic instr_fetch_err_plus2; // Instruction error is misaligned + logic illegal_c_insn_id; // Illegal compressed instruction sent to ID stage + logic [31:0] pc_if; // Program counter in IF stage + logic [31:0] pc_id; // Program counter in ID stage + logic [31:0] pc_wb; // Program counter in WB stage + logic [33:0] imd_val_d_ex[2]; // Intermediate register for multicycle Ops + logic [33:0] imd_val_q_ex[2]; // Intermediate register for multicycle Ops + logic [1:0] imd_val_we_ex; + + logic data_ind_timing; + logic dummy_instr_en; + logic [2:0] dummy_instr_mask; + logic dummy_instr_seed_en; + logic [31:0] dummy_instr_seed; + logic icache_enable; + logic icache_inval; + logic pc_mismatch_alert; + logic csr_shadow_err; + + logic instr_first_cycle_id; + logic instr_valid_clear; + logic pc_set; + logic pc_set_spec; + logic nt_branch_mispredict; + pc_sel_e pc_mux_id; // Mux selector for next PC + exc_pc_sel_e exc_pc_mux_id; // Mux selector for exception PC + exc_cause_e exc_cause; // Exception cause + + logic lsu_load_err; + logic lsu_store_err; + + // LSU signals + logic lsu_addr_incr_req; + logic [31:0] lsu_addr_last; + + // Jump and branch target and decision (EX->IF) + logic [31:0] branch_target_ex; + logic branch_decision; + + // Core busy signals + logic ctrl_busy; + logic if_busy; + logic lsu_busy; + logic core_busy_d, core_busy_q; + + // Register File + logic [4:0] rf_raddr_a; + logic [31:0] rf_rdata_a; + logic [4:0] rf_raddr_b; + logic [31:0] rf_rdata_b; + logic rf_ren_a; + logic rf_ren_b; + logic [4:0] rf_waddr_wb; + logic [31:0] rf_wdata_wb; + // Writeback register write data that can be used on the forwarding path (doesn't factor in memory + // read data as this is too late for the forwarding path) + logic [31:0] rf_wdata_fwd_wb; + logic [31:0] rf_wdata_lsu; + logic rf_we_wb; + logic rf_we_lsu; + + logic [4:0] rf_waddr_id; + logic [31:0] rf_wdata_id; + logic rf_we_id; + logic rf_rd_a_wb_match; + logic rf_rd_b_wb_match; + + // ALU Control + alu_op_e alu_operator_ex; + logic [31:0] alu_operand_a_ex; + logic [31:0] alu_operand_b_ex; + + logic [31:0] bt_a_operand; + logic [31:0] bt_b_operand; + + logic [31:0] alu_adder_result_ex; // Used to forward computed address to LSU + logic [31:0] result_ex; + + // Multiplier Control + logic mult_en_ex; + logic div_en_ex; + logic mult_sel_ex; + logic div_sel_ex; + md_op_e multdiv_operator_ex; + logic [1:0] multdiv_signed_mode_ex; + logic [31:0] multdiv_operand_a_ex; + logic [31:0] multdiv_operand_b_ex; + logic multdiv_ready_id; + + // CSR control + logic csr_access; + csr_op_e csr_op; + logic csr_op_en; + csr_num_e csr_addr; + logic [31:0] csr_rdata; + logic [31:0] csr_wdata; + logic illegal_csr_insn_id; // CSR access to non-existent register, + // with wrong priviledge level, + // or missing write permissions + + // Data Memory Control + logic lsu_we; + logic [1:0] lsu_type; + logic lsu_sign_ext; + logic lsu_req; + logic [31:0] lsu_wdata; + logic lsu_req_done; + + // stall control + logic id_in_ready; + logic ex_valid; + + logic lsu_resp_valid; + logic lsu_resp_err; + + // Signals between instruction core interface and pipe (if and id stages) + logic instr_req_int; // Id stage asserts a req to instruction core interface + + // Writeback stage + logic en_wb; + wb_instr_type_e instr_type_wb; + logic ready_wb; + logic rf_write_wb; + logic outstanding_load_wb; + logic outstanding_store_wb; + + // Interrupts + logic irq_pending; + logic nmi_mode; + irqs_t irqs; + logic csr_mstatus_mie; + logic [31:0] csr_mepc, csr_depc; + + // PMP signals + logic [33:0] csr_pmp_addr [PMPNumRegions]; + pmp_cfg_t csr_pmp_cfg [PMPNumRegions]; + logic pmp_req_err [PMP_NUM_CHAN]; + logic instr_req_out; + logic data_req_out; + + logic csr_save_if; + logic csr_save_id; + logic csr_save_wb; + logic csr_restore_mret_id; + logic csr_restore_dret_id; + logic csr_save_cause; + logic csr_mtvec_init; + logic [31:0] csr_mtvec; + logic [31:0] csr_mtval; + logic csr_mstatus_tw; + priv_lvl_e priv_mode_id; + priv_lvl_e priv_mode_if; + priv_lvl_e priv_mode_lsu; + + // debug mode and dcsr configuration + logic debug_mode; + dbg_cause_e debug_cause; + logic debug_csr_save; + logic debug_single_step; + logic debug_ebreakm; + logic debug_ebreaku; + logic trigger_match; + + // signals relating to instruction movements between pipeline stages + // used by performance counters and RVFI + logic instr_id_done; + logic instr_done_wb; + + logic perf_instr_ret_wb; + logic perf_instr_ret_compressed_wb; + logic perf_iside_wait; + logic perf_dside_wait; + logic perf_mul_wait; + logic perf_div_wait; + logic perf_jump; + logic perf_branch; + logic perf_tbranch; + logic perf_load; + logic perf_store; + + // for RVFI + logic illegal_insn_id, unused_illegal_insn_id; // ID stage sees an illegal instruction + + // RISC-V Formal Interface signals +`ifdef RVFI + logic rvfi_instr_new_wb; + logic rvfi_intr_d; + logic rvfi_intr_q; + logic rvfi_set_trap_pc_d; + logic rvfi_set_trap_pc_q; + logic [31:0] rvfi_insn_id; + logic [4:0] rvfi_rs1_addr_d; + logic [4:0] rvfi_rs1_addr_q; + logic [4:0] rvfi_rs2_addr_d; + logic [4:0] rvfi_rs2_addr_q; + logic [4:0] rvfi_rs3_addr_d; + logic [31:0] rvfi_rs1_data_d; + logic [31:0] rvfi_rs1_data_q; + logic [31:0] rvfi_rs2_data_d; + logic [31:0] rvfi_rs2_data_q; + logic [31:0] rvfi_rs3_data_d; + logic [4:0] rvfi_rd_addr_wb; + logic [4:0] rvfi_rd_addr_q; + logic [4:0] rvfi_rd_addr_d; + logic [31:0] rvfi_rd_wdata_wb; + logic [31:0] rvfi_rd_wdata_d; + logic [31:0] rvfi_rd_wdata_q; + logic rvfi_rd_we_wb; + logic [3:0] rvfi_mem_mask_int; + logic [31:0] rvfi_mem_rdata_d; + logic [31:0] rvfi_mem_rdata_q; + logic [31:0] rvfi_mem_wdata_d; + logic [31:0] rvfi_mem_wdata_q; + logic [31:0] rvfi_mem_addr_d; + logic [31:0] rvfi_mem_addr_q; +`endif + + ////////////////////// + // Clock management // + ////////////////////// + + logic clk; + + logic clock_en; + + // Before going to sleep, wait for I- and D-side + // interfaces to finish ongoing operations. + assign core_busy_d = ctrl_busy | if_busy | lsu_busy | fp_busy; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + core_busy_q <= 1'b0; + end else begin + core_busy_q <= core_busy_d; + end + end + // capture fetch_enable_i in fetch_enable_q, once for ever + logic fetch_enable_q; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fetch_enable_q <= 1'b0; + end else if (fetch_enable_i) begin + fetch_enable_q <= 1'b1; + end + end + + assign clock_en = fetch_enable_q & (core_busy_q | debug_req_i | irq_pending | irq_nm_i); + assign core_sleep_o = ~clock_en; + + // main clock gate of the core + // generates all clocks except the one for the debug unit which is + // independent + prim_clock_gating core_clock_gate_i ( + .clk_i ( clk_i ), + .en_i ( clock_en ), + .test_en_i ( test_en_i ), + .clk_o ( clk ) + ); + + ////////////// + // IF stage // + ////////////// + + brq_ifu #( + .DmHaltAddr ( DmHaltAddr ), + .DmExceptionAddr ( DmExceptionAddr ), + .DummyInstructions ( DummyInstructions ), + .ICache ( ICache ), + .ICacheECC ( ICacheECC ), + .PCIncrCheck ( PCIncrCheck ), + .BranchPredictor ( BranchPredictor ) + ) if_stage_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + + .boot_addr_i ( boot_addr_i ), + .req_i ( instr_req_int ), // instruction request control + + // instruction cache interface + .instr_req_o ( instr_req_out ), + .instr_addr_o ( instr_addr_o ), + .instr_gnt_i ( instr_gnt_i ), + .instr_rvalid_i ( instr_rvalid_i ), + .instr_rdata_i ( instr_rdata_i ), + .instr_err_i ( instr_err_i ), + .instr_pmp_err_i ( pmp_req_err[PMP_I] ), + + // outputs to ID stage + .instr_valid_id_o ( instr_valid_id ), + .instr_new_id_o ( instr_new_id ), + .instr_rdata_id_o ( instr_rdata_id ), + .instr_rdata_alu_id_o ( instr_rdata_alu_id ), + .instr_rdata_c_id_o ( instr_rdata_c_id ), + .instr_is_compressed_id_o ( instr_is_compressed_id ), + .instr_bp_taken_o ( instr_bp_taken_id ), + .instr_fetch_err_o ( instr_fetch_err ), + .instr_fetch_err_plus2_o ( instr_fetch_err_plus2 ), + .illegal_c_insn_id_o ( illegal_c_insn_id ), + .dummy_instr_id_o ( dummy_instr_id ), + .pc_if_o ( pc_if ), + .pc_id_o ( pc_id ), + + // control signals + .instr_valid_clear_i ( instr_valid_clear ), + .pc_set_i ( pc_set ), + .pc_set_spec_i ( pc_set_spec ), + .pc_mux_i ( pc_mux_id ), + .nt_branch_mispredict_i ( nt_branch_mispredict ), + .exc_pc_mux_i ( exc_pc_mux_id ), + .exc_cause ( exc_cause ), + .dummy_instr_en_i ( dummy_instr_en ), + .dummy_instr_mask_i ( dummy_instr_mask ), + .dummy_instr_seed_en_i ( dummy_instr_seed_en ), + .dummy_instr_seed_i ( dummy_instr_seed ), + .icache_enable_i ( icache_enable ), + .icache_inval_i ( icache_inval ), + + // branch targets + .branch_target_ex_i ( branch_target_ex ), + + // CSRs + .csr_mepc_i ( csr_mepc ), // exception return address + .csr_depc_i ( csr_depc ), // debug return address + .csr_mtvec_i ( csr_mtvec ), // trap-vector base address + .csr_mtvec_init_o ( csr_mtvec_init ), + + // pipeline stalls + .id_in_ready_i ( id_in_ready ), + .pc_mismatch_alert_o ( pc_mismatch_alert ), + .if_busy_o ( if_busy ) + ); + + // Core is waiting for the ISide when ID/EX stage is ready for a new instruction but none are + // available + assign perf_iside_wait = id_in_ready & ~instr_valid_id; + + // Qualify the instruction request with PMP error + assign instr_req_o = instr_req_out & ~pmp_req_err[PMP_I]; + + ////////////// + // ID stage // + ////////////// + + brq_idu #( + .RV32E ( RV32E ), + .RV32M ( RV32M ), + .RV32B ( RV32B ), + .BranchTargetALU ( BranchTargetALU ), + .DataIndTiming ( DataIndTiming ), + .SpecBranch ( SpecBranch ), + .WritebackStage ( WritebackStage ), + .BranchPredictor ( BranchPredictor ) + ) id_stage_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + + // Processor Enable + .ctrl_busy_o ( ctrl_busy ), + .illegal_insn_o ( illegal_insn_id ), + + // from/to IF-ID pipeline register + .instr_valid_i ( instr_valid_id ), + .instr_rdata_i ( instr_rdata_id ), + .instr_rdata_alu_i ( instr_rdata_alu_id ), + .instr_rdata_c_i ( instr_rdata_c_id ), + .instr_is_compressed_i ( instr_is_compressed_id ), + .instr_bp_taken_i ( instr_bp_taken_id ), + + // Jumps and branches + .branch_decision_i ( branch_decision ), + + // IF and ID control signals + .instr_first_cycle_id_o ( instr_first_cycle_id ), + .instr_valid_clear_o ( instr_valid_clear ), + .id_in_ready_o ( id_in_ready ), + .instr_req_o ( instr_req_int ), + .pc_set_o ( pc_set ), + .pc_set_spec_o ( pc_set_spec ), + .pc_mux_o ( pc_mux_id ), + .nt_branch_mispredict_o ( nt_branch_mispredict ), + .exc_pc_mux_o ( exc_pc_mux_id ), + .exc_cause_o ( exc_cause ), + .icache_inval_o ( icache_inval ), + + .instr_fetch_err_i ( instr_fetch_err ), + .instr_fetch_err_plus2_i ( instr_fetch_err_plus2 ), + .illegal_c_insn_i ( illegal_c_insn_id ), + + .pc_id_i ( pc_id ), + + // Stalls + .ex_valid_i ( valid_id_fpu ), // changed by zeeshan from + // ex_valid to valid_id_fpu + // for ready selection + .lsu_resp_valid_i ( lsu_resp_valid ), + + .alu_operator_ex_o ( alu_operator_ex ), + .alu_operand_a_ex_o ( alu_operand_a_ex ), + .alu_operand_b_ex_o ( alu_operand_b_ex ), + + .imd_val_q_ex_o ( imd_val_q_ex ), + .imd_val_d_ex_i ( imd_val_d_ex ), + .imd_val_we_ex_i ( imd_val_we_ex ), + + .bt_a_operand_o ( bt_a_operand ), + .bt_b_operand_o ( bt_b_operand ), + + .mult_en_ex_o ( mult_en_ex ), + .div_en_ex_o ( div_en_ex ), + .mult_sel_ex_o ( mult_sel_ex ), + .div_sel_ex_o ( div_sel_ex ), + .multdiv_operator_ex_o ( multdiv_operator_ex ), + .multdiv_signed_mode_ex_o ( multdiv_signed_mode_ex ), + .multdiv_operand_a_ex_o ( multdiv_operand_a_ex ), + .multdiv_operand_b_ex_o ( multdiv_operand_b_ex ), + .multdiv_ready_id_o ( multdiv_ready_id ), + + // CSR ID/EX + .csr_access_o ( csr_access ), + .csr_op_o ( csr_op ), + .csr_op_en_o ( csr_op_en ), + .csr_save_if_o ( csr_save_if ), // control signal to save PC + .csr_save_id_o ( csr_save_id ), // control signal to save PC + .csr_save_wb_o ( csr_save_wb ), // control signal to save PC + .csr_restore_mret_id_o ( csr_restore_mret_id ), // restore mstatus upon MRET + .csr_restore_dret_id_o ( csr_restore_dret_id ), // restore mstatus upon MRET + .csr_save_cause_o ( csr_save_cause ), + .csr_mtval_o ( csr_mtval ), + .priv_mode_i ( priv_mode_id ), + .csr_mstatus_tw_i ( csr_mstatus_tw ), + .illegal_csr_insn_i ( illegal_csr_insn_id ), + .data_ind_timing_i ( data_ind_timing ), + + // LSU + .lsu_req_o ( lsu_req ), // to load store unit + .lsu_we_o ( lsu_we ), // to load store unit + .lsu_type_o ( lsu_type ), // to load store unit + .lsu_sign_ext_o ( lsu_sign_ext ), // to load store unit + .lsu_wdata_o ( lsu_wdata ), // to load store unit + .lsu_req_done_i ( lsu_req_done ), // from load store unit + + .lsu_addr_incr_req_i ( lsu_addr_incr_req ), + .lsu_addr_last_i ( lsu_addr_last ), + + .lsu_load_err_i ( lsu_load_err ), + .lsu_store_err_i ( lsu_store_err ), + + // Interrupt Signals + .csr_mstatus_mie_i ( csr_mstatus_mie ), + .irq_pending_i ( irq_pending ), + .irqs_i ( irqs ), + .irq_nm_i ( irq_nm_i ), + .nmi_mode_o ( nmi_mode ), + + // Debug Signal + .debug_mode_o ( debug_mode ), + .debug_cause_o ( debug_cause ), + .debug_csr_save_o ( debug_csr_save ), + .debug_req_i ( debug_req_i ), + .debug_single_step_i ( debug_single_step ), + .debug_ebreakm_i ( debug_ebreakm ), + .debug_ebreaku_i ( debug_ebreaku ), + .trigger_match_i ( trigger_match ), + + // write data to commit in the register file + .result_ex_i ( data_wb ), // changed by zeeshan from result_ex + // to data_wb for FVCT, FMV.WX ins + .csr_rdata_i ( csr_rdata ), + + .rf_raddr_a_o ( rf_raddr_a ), + .rf_rdata_a_i ( rf_rdata_a ), + .rf_raddr_b_o ( rf_raddr_b ), + .rf_rdata_b_i ( rf_int_fp_lsu ), + .rf_ren_a_o ( rf_ren_a ), + .rf_ren_b_o ( rf_ren_b ), + .rf_waddr_id_o ( rf_waddr_id ), + .rf_wdata_id_o ( rf_wdata_id ), + .rf_we_id_o ( rf_we_id ), + .rf_rd_a_wb_match_o ( rf_rd_a_wb_match ), + .rf_rd_b_wb_match_o ( rf_rd_b_wb_match ), + + .rf_waddr_wb_i ( rf_waddr_wb ), + .rf_wdata_fwd_wb_i ( rf_wdata_fwd_wb ), + .rf_write_wb_i ( rf_write_wb ), + + .en_wb_o ( en_wb ), + .instr_type_wb_o ( instr_type_wb ), + .instr_perf_count_id_o ( instr_perf_count_id ), + .ready_wb_i ( ready_wb ), + .outstanding_load_wb_i ( outstanding_load_wb ), + .outstanding_store_wb_i ( outstanding_store_wb ), + + // Performance Counters + .perf_jump_o ( perf_jump ), + .perf_branch_o ( perf_branch ), + .perf_tbranch_o ( perf_tbranch ), + .perf_dside_wait_o ( perf_dside_wait ), + .perf_mul_wait_o ( perf_mul_wait ), + .perf_div_wait_o ( perf_div_wait ), + .instr_id_done_o ( instr_id_done ), + + // Floating point extensions IO + .fp_rounding_mode_o ( fp_rounding_mode ), // defines the rounding mode + .fp_rf_rdata_a_i ( fp_rf_rdata_a ), + .fp_rf_rdata_b_i ( fp_rf_rdata_b ), + .fp_rf_rdata_c_i ( fp_rf_rdata_c ), + .fp_rf_raddr_a_o ( fp_rf_raddr_a ), + .fp_rf_raddr_b_o ( fp_rf_raddr_b ), + .fp_rf_raddr_c_o ( fp_rf_raddr_c ), + .fp_rf_waddr_o ( fp_rf_waddr_id ), + .fp_rf_we_o ( fp_rf_wen_id ), + .fp_alu_operator_o ( fp_alu_operator ), + .fp_alu_op_mod_o ( fp_alu_op_mod ), + .fp_src_fmt_o ( fp_src_fmt ), + .fp_dst_fmt_o ( fp_dst_fmt ), + .fp_rm_dynamic_o ( fp_rm_dynamic ), + .fp_flush_o ( fp_flush ), + .is_fp_instr_o ( is_fp_instr ), + .use_fp_rs1_o ( use_fp_rs1 ), + .use_fp_rs2_o ( use_fp_rs2 ), + .use_fp_rs3_o ( use_fp_rs3 ), + .use_fp_rd_o ( use_fp_rd ), + .fpu_busy_i ( fpu_busy_idu ), + .fp_rf_write_wb_i ( fp_rf_write_wb ), + .fp_rf_wdata_fwd_wb_i ( fp_rf_wdata_wb ), + .fp_operands_o ( fp_operands ), + .fp_load_o ( fp_load ) + ); + + // for RVFI only + assign unused_illegal_insn_id = illegal_insn_id; + + brq_exu #( + .RV32M ( RV32M ), + .RV32B ( RV32B ), + .BranchTargetALU ( BranchTargetALU ) + ) ex_block_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + + // ALU signal from ID stage + .alu_operator_i ( alu_operator_ex ), + .alu_operand_a_i ( alu_operand_a_ex ), + .alu_operand_b_i ( alu_operand_b_ex ), + .alu_instr_first_cycle_i ( instr_first_cycle_id ), + + // Branch target ALU signal from ID stage + .bt_a_operand_i ( bt_a_operand ), + .bt_b_operand_i ( bt_b_operand ), + + // Multipler/Divider signal from ID stage + .multdiv_operator_i ( multdiv_operator_ex ), + .mult_en_i ( mult_en_ex ), + .div_en_i ( div_en_ex ), + .mult_sel_i ( mult_sel_ex ), + .div_sel_i ( div_sel_ex ), + .multdiv_signed_mode_i ( multdiv_signed_mode_ex ), + .multdiv_operand_a_i ( multdiv_operand_a_ex ), + .multdiv_operand_b_i ( multdiv_operand_b_ex ), + .multdiv_ready_id_i ( multdiv_ready_id ), + .data_ind_timing_i ( data_ind_timing ), + + // Intermediate value register + .imd_val_we_o ( imd_val_we_ex ), + .imd_val_d_o ( imd_val_d_ex ), + .imd_val_q_i ( imd_val_q_ex ), + + // Outputs + .alu_adder_result_ex_o ( alu_adder_result_ex ), // to LSU + .result_ex_o ( result_ex ), // to ID + + .branch_target_o ( branch_target_ex ), // to IF + .branch_decision_o ( branch_decision ), // to ID + + .ex_valid_o ( ex_valid ) + ); + + ///////////////////// + // Load/store unit // + ///////////////////// + + assign data_req_o = data_req_out & ~pmp_req_err[PMP_D]; + assign lsu_resp_err = lsu_load_err | lsu_store_err; + + brq_lsu load_store_unit_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + + // data interface + .data_req_o ( data_req_out ), + .data_gnt_i ( data_gnt_i ), + .data_rvalid_i ( data_rvalid_i ), + .data_err_i ( data_err_i ), + .data_pmp_err_i ( pmp_req_err[PMP_D] ), + + .data_addr_o ( data_addr_o ), + .data_we_o ( data_we_o ), + .data_be_o ( data_be_o ), + .data_wdata_o ( data_wdata_o ), + .data_rdata_i ( data_rdata_i ), + + // signals to/from ID/EX stage + .lsu_we_i ( lsu_we ), + .lsu_type_i ( lsu_type ), + .lsu_wdata_i ( lsu_wdata ), + .lsu_sign_ext_i ( lsu_sign_ext ), + + .lsu_rdata_o ( rf_wdata_lsu ), + .lsu_rdata_valid_o ( rf_we_lsu ), + .lsu_req_i ( lsu_req ), + .lsu_req_done_o ( lsu_req_done ), + + .adder_result_ex_i ( alu_adder_result_ex ), + + .addr_incr_req_o ( lsu_addr_incr_req ), + .addr_last_o ( lsu_addr_last ), + + + .lsu_resp_valid_o ( lsu_resp_valid ), + + // exception signals + .load_err_o ( lsu_load_err ), + .store_err_o ( lsu_store_err ), + + .busy_o ( lsu_busy ), + + .perf_load_o ( perf_load ), + .perf_store_o ( perf_store ) + ); + + brq_wbu #( + .WritebackStage ( WritebackStage ) + ) wb_stage_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + .en_wb_i ( en_wb ), + .instr_type_wb_i ( instr_type_wb ), + .pc_id_i ( pc_id ), + .instr_is_compressed_id_i ( instr_is_compressed_id ), + .instr_perf_count_id_i ( instr_perf_count_id ), + + .ready_wb_o ( ready_wb ), + .rf_write_wb_o ( rf_write_wb ), + .outstanding_load_wb_o ( outstanding_load_wb ), + .outstanding_store_wb_o ( outstanding_store_wb ), + .pc_wb_o ( pc_wb ), + .perf_instr_ret_wb_o ( perf_instr_ret_wb ), + .perf_instr_ret_compressed_wb_o ( perf_instr_ret_compressed_wb ), + + .rf_waddr_id_i ( rf_waddr_id ), + .rf_wdata_id_i ( rf_wdata_id ), + .rf_we_id_i ( rf_we_id ), + + .rf_wdata_lsu_i ( rf_wdata_lsu ), + .rf_we_lsu_i ( rf_we_lsu ), + + .rf_wdata_fwd_wb_o ( rf_wdata_fwd_wb ), + + .rf_waddr_wb_o ( rf_waddr_wb ), + .rf_wdata_wb_o ( rf_wdata_wb ), + .rf_we_wb_o ( rf_we_wb ), + + .lsu_resp_valid_i ( lsu_resp_valid ), + .lsu_resp_err_i ( lsu_resp_err ), + + .instr_done_wb_o ( instr_done_wb ), + + // floating point + .fp_rf_write_wb_o ( fp_rf_write_wb ), + .fp_rf_wen_wb_o ( fp_rf_wen_wb ), + .fp_rf_waddr_wb_o ( fp_rf_waddr_wb ), + .fp_rf_wen_id_i ( fp_rf_wen_id ), + .fp_rf_waddr_id_i ( fp_rf_waddr_id ), + .fp_rf_wdata_wb_o ( fp_rf_wdata_wb ), + .fp_load_i ( fp_load ) + ); + + /////////////////////// + // Register file ECC // + /////////////////////// + + logic [RegFileDataWidth-1:0] rf_wdata_wb_ecc; + logic [RegFileDataWidth-1:0] rf_rdata_a_ecc; + logic [RegFileDataWidth-1:0] rf_rdata_b_ecc; + logic rf_ecc_err_comb; + + if (RegFileECC) begin : gen_regfile_ecc + + logic [1:0] rf_ecc_err_a, rf_ecc_err_b; + logic rf_ecc_err_a_id, rf_ecc_err_b_id; + + // ECC checkbit generation for regiter file wdata + prim_secded_39_32_enc regfile_ecc_enc ( + .in (rf_wdata_wb), + .out (rf_wdata_wb_ecc) + ); + + // ECC checking on register file rdata + prim_secded_39_32_dec regfile_ecc_dec_a ( + .in (rf_rdata_a_ecc), + .d_o (), + .syndrome_o (), + .err_o (rf_ecc_err_a) + ); + prim_secded_39_32_dec regfile_ecc_dec_b ( + .in (rf_rdata_b_ecc), + .d_o (), + .syndrome_o (), + .err_o (rf_ecc_err_b) + ); + + // Assign read outputs - no error correction, just trigger an alert + assign rf_rdata_a = rf_rdata_a_ecc[31:0]; + assign rf_rdata_b = rf_rdata_b_ecc[31:0]; + + // Calculate errors - qualify with WB forwarding to avoid xprop into the alert signal + assign rf_ecc_err_a_id = |rf_ecc_err_a & rf_ren_a & ~rf_rd_a_wb_match; + assign rf_ecc_err_b_id = |rf_ecc_err_b & rf_ren_b & ~rf_rd_b_wb_match; + + // Combined error + assign rf_ecc_err_comb = instr_valid_id & (rf_ecc_err_a_id | rf_ecc_err_b_id); + + end else begin : gen_no_regfile_ecc + logic unused_rf_ren_a, unused_rf_ren_b; + logic unused_rf_rd_a_wb_match, unused_rf_rd_b_wb_match; + + assign unused_rf_ren_a = rf_ren_a; + assign unused_rf_ren_b = rf_ren_b; + assign unused_rf_rd_a_wb_match = rf_rd_a_wb_match; + assign unused_rf_rd_b_wb_match = rf_rd_b_wb_match; + assign rf_wdata_wb_ecc = rf_wdata_wb; + assign rf_rdata_a = rf_rdata_a_ecc; + assign rf_rdata_b = rf_rdata_b_ecc; + assign rf_ecc_err_comb = 1'b0; + end + + assign rf_int_fp_lsu = (is_fp_instr & use_fp_rs2) ? fp_rf_rdata_b : rf_rdata_b; + + if (RegFile == RegFileFF) begin : gen_regfile_ff + brq_register_file_ff #( + .RV32E ( RV32E ), + .DataWidth ( RegFileDataWidth ), + .DummyInstructions ( DummyInstructions ) + ) register_file_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .test_en_i ( test_en_i ), + .dummy_instr_id_i ( dummy_instr_id ), + + .raddr_a_i ( rf_raddr_a ), + .rdata_a_o ( rf_rdata_a_ecc ), + .raddr_b_i ( rf_raddr_b ), + .rdata_b_o ( rf_rdata_b_ecc ), + .waddr_a_i ( rf_waddr_wb ), + .wdata_a_i ( rf_wdata_wb_ecc ), + .we_a_i ( rf_we_wb ) + ); + end else if (RegFile == RegFileFPGA) begin : gen_regfile_fpga + brq_register_file_fpga #( + .RV32E ( RV32E ), + .DataWidth ( RegFileDataWidth ), + .DummyInstructions ( DummyInstructions ) + ) register_file_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .test_en_i ( test_en_i ), + .dummy_instr_id_i ( dummy_instr_id ), + + .raddr_a_i ( rf_raddr_a ), + .rdata_a_o ( rf_rdata_a_ecc ), + .raddr_b_i ( rf_raddr_b ), + .rdata_b_o ( rf_rdata_b_ecc ), + .waddr_a_i ( rf_waddr_wb ), + .wdata_a_i ( rf_wdata_wb_ecc ), + .we_a_i ( rf_we_wb ) + ); + end else if (RegFile == RegFileLatch) begin : gen_regfile_latch + brq_register_file_latch #( + .RV32E ( RV32E ), + .DataWidth ( RegFileDataWidth ), + .DummyInstructions ( DummyInstructions ) + ) register_file_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .test_en_i ( test_en_i ), + .dummy_instr_id_i ( dummy_instr_id ), + + .raddr_a_i ( rf_raddr_a ), + .rdata_a_o ( rf_rdata_a_ecc ), + .raddr_b_i ( rf_raddr_b ), + .rdata_b_o ( rf_rdata_b_ecc ), + .waddr_a_i ( rf_waddr_wb ), + .wdata_a_i ( rf_wdata_wb_ecc ), + .we_a_i ( rf_we_wb ) + ); + end + + if (FloatingPoint) begin : gen_fp_regfile + brq_fp_register_file_ff #( + .RVF ( RVF ), + .DataWidth ( W ) + ) fp_register_file ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .raddr_a_i ( fp_rf_raddr_a ), + .rdata_a_o ( fp_rf_rdata_a ), + + .raddr_b_i ( fp_rf_raddr_b ), + .rdata_b_o ( fp_rf_rdata_b ), + + .raddr_c_i ( fp_rf_raddr_c ), + .rdata_c_o ( fp_rf_rdata_c ), + + .waddr_a_i ( fp_rf_waddr_wb ), + .wdata_a_i ( fp_rf_wdata_wb ), + .we_a_i ( fp_rf_wen_wb ) +); + end + + /////////////////// + // Alert outputs // + /////////////////// + + // Minor alert - core is in a recoverable state + // TODO add I$ ECC errors here + assign alert_minor_o = 1'b0; + + // Major alert - core is unrecoverable + assign alert_major_o = (rf_ecc_err_comb | pc_mismatch_alert | csr_shadow_err); + + + + // Explict INC_ASSERT block to avoid unused signal lint warnings were asserts are not included + `ifdef INC_ASSERT + // Signals used for assertions only + logic outstanding_load_resp; + logic outstanding_store_resp; + + logic outstanding_load_id; + logic outstanding_store_id; + + assign outstanding_load_id = id_stage_i.instr_executing & id_stage_i.lsu_req_dec & + ~id_stage_i.lsu_we; + assign outstanding_store_id = id_stage_i.instr_executing & id_stage_i.lsu_req_dec & + id_stage_i.lsu_we; + + if (WritebackStage) begin : gen_wb_stage + // When the writeback stage is present a load/store could be in ID or WB. A Load/store in ID can + // see a response before it moves to WB when it is unaligned otherwise we should only see + // a response when load/store is in WB. + assign outstanding_load_resp = outstanding_load_wb | + (outstanding_load_id & load_store_unit_i.split_misaligned_access); + + assign outstanding_store_resp = outstanding_store_wb | + (outstanding_store_id & load_store_unit_i.split_misaligned_access); + + // When writing back the result of a load, the load must have made it to writeback + + end else begin : gen_no_wb_stage + // Without writeback stage only look into whether load or store is in ID to determine if + // a response is expected. + assign outstanding_load_resp = outstanding_load_id; + assign outstanding_store_resp = outstanding_store_id; + end + `endif + + + //////////////////////// + // RF (Register File) // + //////////////////////// +`ifdef RVFI + assign rvfi_rd_addr_wb = rf_waddr_wb; + assign rvfi_rd_wdata_wb = rf_we_wb ? rf_wdata_wb : rf_wdata_lsu; + assign rvfi_rd_we_wb = rf_we_wb | rf_we_lsu; +`endif + + + ///////////////////////////////////////// + // CSRs (Control and Status Registers) // + ///////////////////////////////////////// + + assign csr_wdata = alu_operand_a_ex; + assign csr_addr = csr_num_e'(csr_access ? alu_operand_b_ex[11:0] : 12'b0); + + brq_cs_registers #( + .DbgTriggerEn ( DbgTriggerEn ), + .DbgHwBreakNum ( DbgHwBreakNum ), + .DataIndTiming ( DataIndTiming ), + .DummyInstructions ( DummyInstructions ), + .ShadowCSR ( ShadowCSR ), + .ICache ( ICache ), + .MHPMCounterNum ( MHPMCounterNum ), + .MHPMCounterWidth ( MHPMCounterWidth ), + .PMPEnable ( PMPEnable ), + .PMPGranularity ( PMPGranularity ), + .PMPNumRegions ( PMPNumRegions ), + .RV32E ( RV32E ), + .RV32M ( RV32M ) + ) cs_registers_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + + // Hart ID from outside + .hart_id_i ( hart_id_i ), + .priv_mode_id_o ( priv_mode_id ), + .priv_mode_if_o ( priv_mode_if ), + .priv_mode_lsu_o ( priv_mode_lsu ), + + // mtvec + .csr_mtvec_o ( csr_mtvec ), + .csr_mtvec_init_i ( csr_mtvec_init ), + .boot_addr_i ( boot_addr_i ), + + // Interface to CSRs ( SRAM like ) + .csr_access_i ( csr_access ), + .csr_addr_i ( csr_addr ), + .csr_wdata_i ( csr_wdata ), + .csr_op_i ( csr_op ), + .csr_op_en_i ( csr_op_en ), + .csr_rdata_o ( csr_rdata ), + + // Interrupt related control signals + .irq_software_i ( irq_software_i ), + .irq_timer_i ( irq_timer_i ), + .irq_external_i ( irq_external_i ), + .irq_fast_i ( irq_fast_i ), + .nmi_mode_i ( nmi_mode ), + .irq_pending_o ( irq_pending ), + .irqs_o ( irqs ), + .csr_mstatus_mie_o ( csr_mstatus_mie ), + .csr_mstatus_tw_o ( csr_mstatus_tw ), + .csr_mepc_o ( csr_mepc ), + + // PMP + .csr_pmp_cfg_o ( csr_pmp_cfg ), + .csr_pmp_addr_o ( csr_pmp_addr ), + + // debug + .csr_depc_o ( csr_depc ), + .debug_mode_i ( debug_mode ), + .debug_cause_i ( debug_cause ), + .debug_csr_save_i ( debug_csr_save ), + .debug_single_step_o ( debug_single_step ), + .debug_ebreakm_o ( debug_ebreakm ), + .debug_ebreaku_o ( debug_ebreaku ), + .trigger_match_o ( trigger_match ), + + .pc_if_i ( pc_if ), + .pc_id_i ( pc_id ), + .pc_wb_i ( pc_wb ), + + .data_ind_timing_o ( data_ind_timing ), + .dummy_instr_en_o ( dummy_instr_en ), + .dummy_instr_mask_o ( dummy_instr_mask ), + .dummy_instr_seed_en_o ( dummy_instr_seed_en ), + .dummy_instr_seed_o ( dummy_instr_seed ), + .icache_enable_o ( icache_enable ), + .csr_shadow_err_o ( csr_shadow_err ), + + .csr_save_if_i ( csr_save_if ), + .csr_save_id_i ( csr_save_id ), + .csr_save_wb_i ( csr_save_wb ), + .csr_restore_mret_i ( csr_restore_mret_id ), + .csr_restore_dret_i ( csr_restore_dret_id ), + .csr_save_cause_i ( csr_save_cause ), + .csr_mcause_i ( exc_cause ), + .csr_mtval_i ( csr_mtval ), + .illegal_csr_insn_o ( illegal_csr_insn_id ), + + // performance counter related signals + .instr_ret_i ( perf_instr_ret_wb ), + .instr_ret_compressed_i ( perf_instr_ret_compressed_wb ), + .iside_wait_i ( perf_iside_wait ), + .jump_i ( perf_jump ), + .branch_i ( perf_branch ), + .branch_taken_i ( perf_tbranch ), + .mem_load_i ( perf_load ), + .mem_store_i ( perf_store ), + .dside_wait_i ( perf_dside_wait ), + .mul_wait_i ( perf_mul_wait ), + .div_wait_i ( perf_div_wait ), + + // floating point + .fp_rm_dynamic_i ( fp_rm_dynamic ), + .fp_frm_o ( fp_frm_csr ), + .fp_status_i ( fp_status ), + .is_fp_instr_i ( is_fp_instr ) + ); + + assign fp_frm_fpnew = fp_rm_dynamic ? fp_frm_csr : fp_rounding_mode; + assign in_ready_c2fpu = id_in_ready; //multdiv_ready_id; + assign in_valid_c2fpu = (instr_valid_id & is_fp_instr); + // assign ready_id_fpu = id_in_ready; // (is_fp_instr) ? out_ready_fpu2c : id_in_ready; + assign valid_id_fpu = (is_fp_instr) ? out_valid_fpu2c : ex_valid; + +// FPU instance + fpnew_top #( + .Features ( fpnew_pkg::RV32F ), + .Implementation ( fpnew_pkg::DEFAULT_NOREGS ), + .TagType ( logic ) + ) i_fpnew_top ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + .operands_i ( fp_operands ), + .rnd_mode_i ( fp_frm_fpnew ), + .op_i ( fp_alu_operator ), + .op_mod_i ( fp_alu_op_mod ), + .src_fmt_i ( fp_src_fmt ), + .dst_fmt_i ( fp_dst_fmt ), + .int_fmt_i ( fpnew_pkg::INT32 ), + .vectorial_op_i ( 1'b0 ), + .tag_i ( '1 ), + .in_valid_i ( in_valid_c2fpu ), + .in_ready_o ( out_ready_fpu2c ), + .flush_i ( fp_flush ), + .result_o ( fp_result ), + .status_o ( fp_status ), + .tag_o ( ), + .out_valid_o ( out_valid_fpu2c ), + .out_ready_i ( in_ready_c2fpu ), + .busy_o ( fp_busy ) + ); + + assign fpu_busy_idu = fp_busy & (~out_valid_fpu2c); + assign data_wb = is_fp_instr ? fp_result : result_ex; + + // These assertions are in top-level as instr_valid_id required as the enable term + + if (PMPEnable) begin : g_pmp + logic [33:0] pmp_req_addr [PMP_NUM_CHAN]; + pmp_req_e pmp_req_type [PMP_NUM_CHAN]; + priv_lvl_e pmp_priv_lvl [PMP_NUM_CHAN]; + + assign pmp_req_addr[PMP_I] = {2'b00,instr_addr_o[31:0]}; + assign pmp_req_type[PMP_I] = PMP_ACC_EXEC; + assign pmp_priv_lvl[PMP_I] = priv_mode_if; + assign pmp_req_addr[PMP_D] = {2'b00,data_addr_o[31:0]}; + assign pmp_req_type[PMP_D] = data_we_o ? PMP_ACC_WRITE : PMP_ACC_READ; + assign pmp_priv_lvl[PMP_D] = priv_mode_lsu; + + brq_pmp #( + .PMPGranularity ( PMPGranularity ), + .PMPNumChan ( PMP_NUM_CHAN ), + .PMPNumRegions ( PMPNumRegions ) + ) pmp_i ( + .clk_i ( clk ), + .rst_ni ( rst_ni ), + // Interface to CSRs + .csr_pmp_cfg_i ( csr_pmp_cfg ), + .csr_pmp_addr_i ( csr_pmp_addr ), + .priv_mode_i ( pmp_priv_lvl ), + // Access checking channels + .pmp_req_addr_i ( pmp_req_addr ), + .pmp_req_type_i ( pmp_req_type ), + .pmp_req_err_o ( pmp_req_err ) + ); + end else begin : g_no_pmp + // Unused signal tieoff + priv_lvl_e unused_priv_lvl_if, unused_priv_lvl_ls; + logic [33:0] unused_csr_pmp_addr [PMPNumRegions]; + pmp_cfg_t unused_csr_pmp_cfg [PMPNumRegions]; + assign unused_priv_lvl_if = priv_mode_if; + assign unused_priv_lvl_ls = priv_mode_lsu; + assign unused_csr_pmp_addr = csr_pmp_addr; + assign unused_csr_pmp_cfg = csr_pmp_cfg; + + // Output tieoff + assign pmp_req_err[PMP_I] = 1'b0; + assign pmp_req_err[PMP_D] = 1'b0; + end + +`ifdef RVFI + // When writeback stage is present RVFI information is emitted when instruction is finished in + // third stage but some information must be captured whilst the instruction is in the second + // stage. Without writeback stage RVFI information is all emitted when instruction retires in + // second stage. RVFI outputs are all straight from flops. So 2 stage pipeline requires a single + // set of flops (instr_info => RVFI_out), 3 stage pipeline requires two sets (instr_info => wb + // => RVFI_out) + localparam int RVFI_STAGES = WritebackStage ? 2 : 1; + + logic rvfi_stage_valid [RVFI_STAGES]; + logic [63:0] rvfi_stage_order [RVFI_STAGES]; + logic [31:0] rvfi_stage_insn [RVFI_STAGES]; + logic rvfi_stage_trap [RVFI_STAGES]; + logic rvfi_stage_halt [RVFI_STAGES]; + logic rvfi_stage_intr [RVFI_STAGES]; + logic [ 1:0] rvfi_stage_mode [RVFI_STAGES]; + logic [ 1:0] rvfi_stage_ixl [RVFI_STAGES]; + logic [ 4:0] rvfi_stage_rs1_addr [RVFI_STAGES]; + logic [ 4:0] rvfi_stage_rs2_addr [RVFI_STAGES]; + logic [ 4:0] rvfi_stage_rs3_addr [RVFI_STAGES]; + logic [31:0] rvfi_stage_rs1_rdata [RVFI_STAGES]; + logic [31:0] rvfi_stage_rs2_rdata [RVFI_STAGES]; + logic [31:0] rvfi_stage_rs3_rdata [RVFI_STAGES]; + logic [ 4:0] rvfi_stage_rd_addr [RVFI_STAGES]; + logic [31:0] rvfi_stage_rd_wdata [RVFI_STAGES]; + logic [31:0] rvfi_stage_pc_rdata [RVFI_STAGES]; + logic [31:0] rvfi_stage_pc_wdata [RVFI_STAGES]; + logic [31:0] rvfi_stage_mem_addr [RVFI_STAGES]; + logic [ 3:0] rvfi_stage_mem_rmask [RVFI_STAGES]; + logic [ 3:0] rvfi_stage_mem_wmask [RVFI_STAGES]; + logic [31:0] rvfi_stage_mem_rdata [RVFI_STAGES]; + logic [31:0] rvfi_stage_mem_wdata [RVFI_STAGES]; + + logic rvfi_stage_valid_d [RVFI_STAGES]; + + assign rvfi_valid = rvfi_stage_valid [RVFI_STAGES-1]; + assign rvfi_order = rvfi_stage_order [RVFI_STAGES-1]; + assign rvfi_insn = rvfi_stage_insn [RVFI_STAGES-1]; + assign rvfi_trap = rvfi_stage_trap [RVFI_STAGES-1]; + assign rvfi_halt = rvfi_stage_halt [RVFI_STAGES-1]; + assign rvfi_intr = rvfi_stage_intr [RVFI_STAGES-1]; + assign rvfi_mode = rvfi_stage_mode [RVFI_STAGES-1]; + assign rvfi_ixl = rvfi_stage_ixl [RVFI_STAGES-1]; + assign rvfi_rs1_addr = rvfi_stage_rs1_addr [RVFI_STAGES-1]; + assign rvfi_rs2_addr = rvfi_stage_rs2_addr [RVFI_STAGES-1]; + assign rvfi_rs3_addr = rvfi_stage_rs3_addr [RVFI_STAGES-1]; + assign rvfi_rs1_rdata = rvfi_stage_rs1_rdata[RVFI_STAGES-1]; + assign rvfi_rs2_rdata = rvfi_stage_rs2_rdata[RVFI_STAGES-1]; + assign rvfi_rs3_rdata = rvfi_stage_rs3_rdata[RVFI_STAGES-1]; + assign rvfi_rd_addr = rvfi_stage_rd_addr [RVFI_STAGES-1]; + assign rvfi_rd_wdata = rvfi_stage_rd_wdata [RVFI_STAGES-1]; + assign rvfi_pc_rdata = rvfi_stage_pc_rdata [RVFI_STAGES-1]; + assign rvfi_pc_wdata = rvfi_stage_pc_wdata [RVFI_STAGES-1]; + assign rvfi_mem_addr = rvfi_stage_mem_addr [RVFI_STAGES-1]; + assign rvfi_mem_rmask = rvfi_stage_mem_rmask[RVFI_STAGES-1]; + assign rvfi_mem_wmask = rvfi_stage_mem_wmask[RVFI_STAGES-1]; + assign rvfi_mem_rdata = rvfi_stage_mem_rdata[RVFI_STAGES-1]; + assign rvfi_mem_wdata = rvfi_stage_mem_wdata[RVFI_STAGES-1]; + + if (WritebackStage) begin : gen_rvfi_wb_stage + logic unused_instr_new_id; + + assign unused_instr_new_id = instr_new_id; + + // With writeback stage first RVFI stage buffers instruction information captured in ID/EX + // awaiting instruction retirement and RF Write data/Mem read data whilst instruction is in WB + // So first stage becomes valid when instruction leaves ID/EX stage and remains valid until + // instruction leaves WB + assign rvfi_stage_valid_d[0] = (instr_id_done & ~dummy_instr_id) | + (rvfi_stage_valid[0] & ~instr_done_wb); + // Second stage is output stage so simple valid cycle after instruction leaves WB (and so has + // retired) + assign rvfi_stage_valid_d[1] = instr_done_wb; + + // Signal new instruction in WB cycle after instruction leaves ID/EX (to enter WB) + logic rvfi_instr_new_wb_q; + + assign rvfi_instr_new_wb = rvfi_instr_new_wb_q; + + always_ff @(posedge clk or negedge rst_ni) begin + if (~rst_ni) begin + rvfi_instr_new_wb_q <= 0; + end else begin + rvfi_instr_new_wb_q <= instr_id_done; + end + end + end else begin : gen_rvfi_no_wb_stage + // Without writeback stage first RVFI stage is output stage so simply valid the cycle after + // instruction leaves ID/EX (and so has retired) + assign rvfi_stage_valid_d[0] = instr_id_done & ~dummy_instr_id; + // Without writeback stage signal new instr_new_wb when instruction enters ID/EX to correctly + // setup register write signals + assign rvfi_instr_new_wb = instr_new_id; + end + + for (genvar i = 0;i < RVFI_STAGES; i = i + 1) begin : g_rvfi_stages + always_ff @(posedge clk or negedge rst_ni) begin + if (!rst_ni) begin + rvfi_stage_halt[i] <= '0; + rvfi_stage_trap[i] <= '0; + rvfi_stage_intr[i] <= '0; + rvfi_stage_order[i] <= '0; + rvfi_stage_insn[i] <= '0; + rvfi_stage_mode[i] <= {PRIV_LVL_M}; + rvfi_stage_ixl[i] <= CSR_MISA_MXL; + rvfi_stage_rs1_addr[i] <= '0; + rvfi_stage_rs2_addr[i] <= '0; + rvfi_stage_rs3_addr[i] <= '0; + rvfi_stage_pc_rdata[i] <= '0; + rvfi_stage_pc_wdata[i] <= '0; + rvfi_stage_mem_rmask[i] <= '0; + rvfi_stage_mem_wmask[i] <= '0; + rvfi_stage_valid[i] <= '0; + rvfi_stage_rs1_rdata[i] <= '0; + rvfi_stage_rs2_rdata[i] <= '0; + rvfi_stage_rs3_rdata[i] <= '0; + rvfi_stage_rd_wdata[i] <= '0; + rvfi_stage_rd_addr[i] <= '0; + rvfi_stage_mem_rdata[i] <= '0; + rvfi_stage_mem_wdata[i] <= '0; + rvfi_stage_mem_addr[i] <= '0; + end else begin + rvfi_stage_valid[i] <= rvfi_stage_valid_d[i]; + + if (i == 0) begin + if(instr_id_done) begin + rvfi_stage_halt[i] <= '0; + rvfi_stage_trap[i] <= illegal_insn_id; + rvfi_stage_intr[i] <= rvfi_intr_d; + rvfi_stage_order[i] <= rvfi_stage_order[i] + 64'(rvfi_stage_valid_d[i]); + rvfi_stage_insn[i] <= rvfi_insn_id; + rvfi_stage_mode[i] <= {priv_mode_id}; + rvfi_stage_ixl[i] <= CSR_MISA_MXL; + rvfi_stage_rs1_addr[i] <= rvfi_rs1_addr_d; + rvfi_stage_rs2_addr[i] <= rvfi_rs2_addr_d; + rvfi_stage_rs3_addr[i] <= rvfi_rs3_addr_d; + rvfi_stage_pc_rdata[i] <= pc_id; + rvfi_stage_pc_wdata[i] <= pc_set ? branch_target_ex : pc_if; + rvfi_stage_mem_rmask[i] <= rvfi_mem_mask_int; + rvfi_stage_mem_wmask[i] <= data_we_o ? rvfi_mem_mask_int : 4'b0000; + rvfi_stage_rs1_rdata[i] <= rvfi_rs1_data_d; + rvfi_stage_rs2_rdata[i] <= rvfi_rs2_data_d; + rvfi_stage_rs3_rdata[i] <= rvfi_rs3_data_d; + rvfi_stage_rd_addr[i] <= rvfi_rd_addr_d; + rvfi_stage_rd_wdata[i] <= rvfi_rd_wdata_d; + rvfi_stage_mem_rdata[i] <= rvfi_mem_rdata_d; + rvfi_stage_mem_wdata[i] <= rvfi_mem_wdata_d; + rvfi_stage_mem_addr[i] <= rvfi_mem_addr_d; + end + end else begin + if(instr_done_wb) begin + rvfi_stage_halt[i] <= rvfi_stage_halt[i-1]; + rvfi_stage_trap[i] <= rvfi_stage_trap[i-1]; + rvfi_stage_intr[i] <= rvfi_stage_intr[i-1]; + rvfi_stage_order[i] <= rvfi_stage_order[i-1]; + rvfi_stage_insn[i] <= rvfi_stage_insn[i-1]; + rvfi_stage_mode[i] <= rvfi_stage_mode[i-1]; + rvfi_stage_ixl[i] <= rvfi_stage_ixl[i-1]; + rvfi_stage_rs1_addr[i] <= rvfi_stage_rs1_addr[i-1]; + rvfi_stage_rs2_addr[i] <= rvfi_stage_rs2_addr[i-1]; + rvfi_stage_rs3_addr[i] <= rvfi_stage_rs3_addr[i-1]; + rvfi_stage_pc_rdata[i] <= rvfi_stage_pc_rdata[i-1]; + rvfi_stage_pc_wdata[i] <= rvfi_stage_pc_wdata[i-1]; + rvfi_stage_mem_rmask[i] <= rvfi_stage_mem_rmask[i-1]; + rvfi_stage_mem_wmask[i] <= rvfi_stage_mem_wmask[i-1]; + rvfi_stage_rs1_rdata[i] <= rvfi_stage_rs1_rdata[i-1]; + rvfi_stage_rs2_rdata[i] <= rvfi_stage_rs2_rdata[i-1]; + rvfi_stage_rs3_rdata[i] <= rvfi_stage_rs3_rdata[i-1]; + rvfi_stage_mem_wdata[i] <= rvfi_stage_mem_wdata[i-1]; + rvfi_stage_mem_addr[i] <= rvfi_stage_mem_addr[i-1]; + + // For 2 RVFI_STAGES/Writeback Stage ignore first stage flops for rd_addr, rd_wdata and + // mem_rdata. For RF write addr/data actual write happens in writeback so capture + // address/data there. For mem_rdata that is only available from the writeback stage. + // Previous stage flops still exist in RTL as they are used by the non writeback config + rvfi_stage_rd_addr[i] <= rvfi_rd_addr_d; + rvfi_stage_rd_wdata[i] <= rvfi_rd_wdata_d; + rvfi_stage_mem_rdata[i] <= rvfi_mem_rdata_d; + end + end + end + end + end + + + // Memory adddress/write data available first cycle of ld/st instruction from register read + always_comb begin + if (instr_first_cycle_id) begin + rvfi_mem_addr_d = alu_adder_result_ex; + rvfi_mem_wdata_d = lsu_wdata; + end else begin + rvfi_mem_addr_d = rvfi_mem_addr_q; + rvfi_mem_wdata_d = rvfi_mem_wdata_q; + end + end + + // Capture read data from LSU when it becomes valid + always_comb begin + if (lsu_resp_valid) begin + rvfi_mem_rdata_d = rf_wdata_lsu; + end else begin + rvfi_mem_rdata_d = rvfi_mem_rdata_q; + end + end + + always_ff @(posedge clk or negedge rst_ni) begin + if (!rst_ni) begin + rvfi_mem_addr_q <= '0; + rvfi_mem_rdata_q <= '0; + rvfi_mem_wdata_q <= '0; + end else begin + rvfi_mem_addr_q <= rvfi_mem_addr_d; + rvfi_mem_rdata_q <= rvfi_mem_rdata_d; + rvfi_mem_wdata_q <= rvfi_mem_wdata_d; + end + end + // Byte enable based on data type + always_comb begin + unique case (lsu_type) + 2'b00: rvfi_mem_mask_int = 4'b1111; + 2'b01: rvfi_mem_mask_int = 4'b0011; + 2'b10: rvfi_mem_mask_int = 4'b0001; + default: rvfi_mem_mask_int = 4'b0000; + endcase + end + + always_comb begin + if (instr_is_compressed_id) begin + rvfi_insn_id = {16'b0, instr_rdata_c_id}; + end else begin + rvfi_insn_id = instr_rdata_id; + end + end + + // Source registers 1 and 2 are read in the first instruction cycle + // Source register 3 is read in the second instruction cycle. + always_comb begin + if (instr_first_cycle_id) begin + rvfi_rs1_data_d = rf_ren_a ? multdiv_operand_a_ex : '0; + rvfi_rs1_addr_d = rf_ren_a ? rf_raddr_a : '0; + rvfi_rs2_data_d = rf_ren_b ? multdiv_operand_b_ex : '0; + rvfi_rs2_addr_d = rf_ren_b ? rf_raddr_b : '0; + rvfi_rs3_data_d = '0; + rvfi_rs3_addr_d = '0; + end else begin + rvfi_rs1_data_d = rvfi_rs1_data_q; + rvfi_rs1_addr_d = rvfi_rs1_addr_q; + rvfi_rs2_data_d = rvfi_rs2_data_q; + rvfi_rs2_addr_d = rvfi_rs2_addr_q; + rvfi_rs3_data_d = multdiv_operand_a_ex; + rvfi_rs3_addr_d = rf_raddr_a; + end + end + always_ff @(posedge clk or negedge rst_ni) begin + if (!rst_ni) begin + rvfi_rs1_data_q <= '0; + rvfi_rs1_addr_q <= '0; + rvfi_rs2_data_q <= '0; + rvfi_rs2_addr_q <= '0; + + end else begin + rvfi_rs1_data_q <= rvfi_rs1_data_d; + rvfi_rs1_addr_q <= rvfi_rs1_addr_d; + rvfi_rs2_data_q <= rvfi_rs2_data_d; + rvfi_rs2_addr_q <= rvfi_rs2_addr_d; + end + end + + always_comb begin + if(rvfi_rd_we_wb) begin + // Capture address/data of write to register file + rvfi_rd_addr_d = rvfi_rd_addr_wb; + // If writing to x0 zero write data as required by RVFI specification + if(rvfi_rd_addr_wb == 5'b0) begin + rvfi_rd_wdata_d = '0; + end else begin + rvfi_rd_wdata_d = rvfi_rd_wdata_wb; + end + end else if(rvfi_instr_new_wb) begin + // If no RF write but new instruction in Writeback (when present) or ID/EX (when no writeback + // stage present) then zero RF write address/data as required by RVFI specification + rvfi_rd_addr_d = '0; + rvfi_rd_wdata_d = '0; + end else begin + // Otherwise maintain previous value + rvfi_rd_addr_d = rvfi_rd_addr_q; + rvfi_rd_wdata_d = rvfi_rd_wdata_q; + end + end + + // RD write register is refreshed only once per cycle and + // then it is kept stable for the cycle. + always_ff @(posedge clk or negedge rst_ni) begin + if (!rst_ni) begin + rvfi_rd_addr_q <= '0; + rvfi_rd_wdata_q <= '0; + end else begin + rvfi_rd_addr_q <= rvfi_rd_addr_d; + rvfi_rd_wdata_q <= rvfi_rd_wdata_d; + end + end + + // rvfi_intr must be set for first instruction that is part of a trap handler. + // On the first cycle of a new instruction see if a trap PC was set by the previous instruction, + // otherwise maintain value. + assign rvfi_intr_d = instr_first_cycle_id ? rvfi_set_trap_pc_q : rvfi_intr_q; + + always_comb begin + rvfi_set_trap_pc_d = rvfi_set_trap_pc_q; + + if (pc_set && pc_mux_id == PC_EXC && + (exc_pc_mux_id == EXC_PC_EXC || exc_pc_mux_id == EXC_PC_IRQ)) begin + // PC is set to enter a trap handler + rvfi_set_trap_pc_d = 1'b1; + end else if (rvfi_set_trap_pc_q && instr_id_done) begin + // first instruction has been executed after PC is set to trap handler + rvfi_set_trap_pc_d = 1'b0; + end + end + + always_ff @(posedge clk or negedge rst_ni) begin + if (!rst_ni) begin + rvfi_set_trap_pc_q <= 1'b0; + rvfi_intr_q <= 1'b0; + end else begin + rvfi_set_trap_pc_q <= rvfi_set_trap_pc_d; + rvfi_intr_q <= rvfi_intr_d; + end + end + +`else + logic unused_instr_new_id, unused_instr_done_wb; + assign unused_instr_new_id = instr_new_id; + assign unused_instr_done_wb = instr_done_wb; +`endif + + + +endmodule
diff --git a/verilog/rtl/brq_core_top.sv b/verilog/rtl/brq_core_top.sv new file mode 100644 index 0000000..65b428a --- /dev/null +++ b/verilog/rtl/brq_core_top.sv
@@ -0,0 +1,215 @@ +//`include "/home/usman/Documents/ibex/rtl/ibex_pkg.sv" +//`include "/home/merl/Documents/ibex/rtl/prim_pkg.sv" + +module brq_core_top #( + parameter bit PMPEnable = 1'b0, + parameter int unsigned PMPGranularity = 0, + parameter int unsigned PMPNumRegions = 0, + parameter int unsigned MHPMCounterNum = 0, + parameter int unsigned MHPMCounterWidth = 40, + parameter bit RV32E = 1'b0, + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, + parameter brq_pkg::rv32b_e RV32B = brq_pkg::RV32BNone, + parameter brq_pkg::regfile_e RegFile = brq_pkg::RegFileFF, + parameter bit BranchTargetALU = 1'b0, + parameter bit WritebackStage = 1'b1, + parameter bit ICache = 1'b0, + parameter bit ICacheECC = 1'b0, + parameter bit BranchPredictor = 1'b0, + parameter bit DbgTriggerEn = 1'b0, + parameter int unsigned DbgHwBreakNum = 1, + parameter bit Securebrq = 1'b0, + parameter int unsigned DmHaltAddr = 0, + parameter int unsigned DmExceptionAddr = 0 +) +( + input clk_i, + input rst_ni, + + // instruction memory interface + input tlul_pkg::tl_d2h_t tl_i_i, + output tlul_pkg::tl_h2d_t tl_i_o, + + // data memory interface + input tlul_pkg::tl_d2h_t tl_d_i, + output tlul_pkg::tl_h2d_t tl_d_o, + + input logic test_en_i, // enable all clk_i gates for testing + + input logic [31:0] hart_id_i, + input logic [31:0] boot_addr_i, + + // Interrupt inputs + input logic irq_software_i, + input logic irq_timer_i, + input logic irq_external_i, + input logic [14:0] irq_fast_i, + input logic irq_nm_i, // non-maskeable interrupt + + // Debug Interface + input logic debug_req_i, + + // CPU Control Signals + input logic fetch_enable_i, + output logic alert_minor_o, + output logic alert_major_o, + output logic core_sleep_o +); +import brq_pkg::*; + + //logic rst_ni; + //assign rst_ni = rst_ni; + // Instruction interface (internal) + logic instr_req; + logic instr_gnt; + logic instr_rvalid; + logic [31:0] instr_addr; + logic [31:0] instr_rdata; + logic instr_err; + + // Data interface (internal) + logic data_req; + logic data_gnt; + logic data_rvalid; + logic data_we; + logic [3:0] data_be; + logic [31:0] data_addr; + logic [31:0] data_wdata; + logic [31:0] data_rdata; + logic data_err; + + + +brq_core #( + .PMPEnable (PMPEnable), + .PMPGranularity (PMPGranularity), + .PMPNumRegions (PMPNumRegions), + .MHPMCounterNum (MHPMCounterNum), + .MHPMCounterWidth (MHPMCounterWidth), + .RV32E (RV32E), + .RV32M (RV32M), + .RV32B (RV32B), + .RegFile (RegFile), + .BranchTargetALU (BranchTargetALU), + .WritebackStage (WritebackStage), + .ICache (ICache), + .ICacheECC (ICacheECC), + .BranchPredictor (BranchPredictor), + .DbgTriggerEn (DbgTriggerEn), + .DbgHwBreakNum (DbgHwBreakNum), + .Securebrq (Securebrq), + .DmHaltAddr (DmHaltAddr), + .DmExceptionAddr (DmExceptionAddr) +) u_core ( + // clk_i and rst_ni + .clk_i (clk_i), + .rst_ni(rst_ni), + + .test_en_i (test_en_i), // enable all clk_i gates for testing + + .hart_id_i (hart_id_i), + .boot_addr_i(boot_addr_i), + + // Instruction memory interface + .instr_req_o (instr_req), + .instr_gnt_i (instr_gnt), + .instr_rvalid_i (instr_rvalid), + .instr_addr_o (instr_addr), + .instr_rdata_i (instr_rdata), + .instr_err_i (instr_err), + + // Data memory interface + .data_req_o (data_req), + .data_gnt_i (data_gnt), + .data_rvalid_i (data_rvalid), + .data_we_o (data_we), + .data_be_o (data_be), + .data_addr_o (data_addr), + .data_wdata_o (data_wdata), + .data_rdata_i (data_rdata), + .data_err_i (data_err), + + // Interrupt inputs + .irq_software_i (irq_software_i), + .irq_timer_i (irq_timer_i), + .irq_external_i (irq_external_i), + .irq_fast_i (irq_fast_i), + .irq_nm_i (irq_nm_i), // non-maskeable interrupt + + // Debug Interface + .debug_req_i (debug_req_i), + + // RISC-V Formal Interface + // Does not comply with the coding standards of _i/_o suffixes, but follows + // the convention of RISC-V Formal Interface Specification. +`ifdef RVFI + .rvfi_valid (), + .rvfi_order (), + .rvfi_insn (), + .rvfi_trap (), + .rvfi_halt (), + .rvfi_intr (), + .rvfi_mode (), + .rvfi_ixl (), + .rvfi_rs1_addr (), + .rvfi_rs2_addr (), + .rvfi_rs3_addr (), + .rvfi_rs1_rdata (), + .rvfi_rs2_rdata (), + .rvfi_rs3_rdata (), + .rvfi_rd_addr (), + .rvfi_rd_wdata (), + .rvfi_pc_rdata (), + .rvfi_pc_wdata (), + .rvfi_mem_addr (), + .rvfi_mem_rmask (), + .rvfi_mem_wmask (), + .rvfi_mem_rdata (), + .rvfi_mem_wdata (), +`endif + + // CPU Control Signals + .fetch_enable_i (fetch_enable_i), + .alert_minor_o (alert_minor_o), + .alert_major_o (alert_major_o), + .core_sleep_o (core_sleep_o) +); + +tlul_host_adapter #( + .MAX_REQS(2) +) intr_interface ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (instr_req), + .gnt_o (instr_gnt), + .addr_i (instr_addr), + .we_i (1'b0), + .wdata_i (32'b0), + .be_i (4'hF), + .valid_o (instr_rvalid), + .rdata_o (instr_rdata), + .err_o (instr_err), + .tl_h_c_a (tl_i_o), + .tl_h_c_d (tl_i_i) +); + +tlul_host_adapter #( + .MAX_REQS (2) +) data_interface ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (data_req), + .gnt_o (data_gnt), + .addr_i (data_addr), + .we_i (data_we), + .wdata_i (data_wdata), + .be_i (data_be), + .valid_o (data_rvalid), + .rdata_o (data_rdata), + .err_o (data_err), + .tl_h_c_a (tl_d_o), + .tl_h_c_d (tl_d_i) +); + + +endmodule
diff --git a/verilog/rtl/brq_counter.sv b/verilog/rtl/brq_counter.sv new file mode 100644 index 0000000..3fedb28 --- /dev/null +++ b/verilog/rtl/brq_counter.sv
@@ -0,0 +1,82 @@ +module brq_counter #( + parameter int CounterWidth = 32 +) ( + input logic clk_i, + input logic rst_ni, + + input logic counter_inc_i, + input logic counterh_we_i, + input logic counter_we_i, + input logic [31:0] counter_val_i, + output logic [63:0] counter_val_o +); + + logic [63:0] counter; + logic [CounterWidth-1:0] counter_upd; + logic [63:0] counter_load; + logic we; + logic [CounterWidth-1:0] counter_d; + + // Update + always_comb begin + + // Write + we = counter_we_i | counterh_we_i; + counter_load[63:32] = counter[63:32]; + counter_load[31:0] = counter_val_i; + if (counterh_we_i) begin + counter_load[63:32] = counter_val_i; + counter_load[31:0] = counter[31:0]; + end + + // Increment + counter_upd = counter[CounterWidth-1:0] + {{CounterWidth-1{1'b0}},1'b1}; + + // Next value logic + if (we) begin + counter_d = counter_load[CounterWidth-1:0]; + end else if (counter_inc_i)begin + counter_d = counter_upd[CounterWidth-1:0]; + end else begin + counter_d = counter[CounterWidth-1:0]; + end + end + +`ifdef FPGA_XILINX + // Set DSP pragma for supported xilinx FPGAs + localparam int DspPragma = CounterWidth < 49 ? "yes" : "no"; + (* use_dsp = DspPragma *) logic [CounterWidth-1:0] counter_q; + + // DSP output register requires synchronous reset. + `define COUNTER_FLOP_RST posedge clk_i +`else + logic [CounterWidth-1:0] counter_q; + + `define COUNTER_FLOP_RST posedge clk_i or negedge rst_ni +`endif + + // Counter flop + always_ff @(`COUNTER_FLOP_RST) begin + if (!rst_ni) begin + counter_q <= '0; + end else begin + counter_q <= counter_d; + end + end + + if (CounterWidth < 64) begin : g_counter_narrow + logic [63:CounterWidth] unused_counter_load; + + assign counter[CounterWidth-1:0] = counter_q; + assign counter[63:CounterWidth] = '0; + assign unused_counter_load = counter_load[63:CounterWidth]; + end else begin : g_counter_full + assign counter = counter_q; + end + + assign counter_val_o = counter; + +endmodule + +// Keep helper defines file-local. +`undef COUNTER_FLOP_RST
diff --git a/verilog/rtl/brq_cs_registers.sv b/verilog/rtl/brq_cs_registers.sv new file mode 100644 index 0000000..6b33279 --- /dev/null +++ b/verilog/rtl/brq_cs_registers.sv
@@ -0,0 +1,1510 @@ + +/** + * Control and Status Registers + * + * Control and Status Registers (CSRs) following the RISC-V Privileged + * Specification, draft version 1.11 + */ + +module brq_cs_registers #( + parameter bit DbgTriggerEn = 0, + parameter int unsigned DbgHwBreakNum = 1, + parameter bit DataIndTiming = 1'b0, + parameter bit DummyInstructions = 1'b0, + parameter bit ShadowCSR = 1'b0, + parameter bit ICache = 1'b0, + parameter int unsigned MHPMCounterNum = 10, + parameter int unsigned MHPMCounterWidth = 40, + parameter bit PMPEnable = 0, + parameter int unsigned PMPGranularity = 0, + parameter int unsigned PMPNumRegions = 4, + parameter bit RV32E = 0, + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, + parameter brq_pkg::rvfloat_e RVF = brq_pkg::RV64FDouble // for floating point +) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + // Hart ID + input logic [31:0] hart_id_i, + + // Privilege mode + output brq_pkg::priv_lvl_e priv_mode_id_o, + output brq_pkg::priv_lvl_e priv_mode_if_o, + output brq_pkg::priv_lvl_e priv_mode_lsu_o, + output logic csr_mstatus_tw_o, + + // mtvec + output logic [31:0] csr_mtvec_o, + input logic csr_mtvec_init_i, + input logic [31:0] boot_addr_i, + + // Interface to registers (SRAM like) + input logic csr_access_i, + input brq_pkg::csr_num_e csr_addr_i, + input logic [31:0] csr_wdata_i, + input brq_pkg::csr_op_e csr_op_i, + input csr_op_en_i, + output logic [31:0] csr_rdata_o, + + // interrupts + input logic irq_software_i, + input logic irq_timer_i, + input logic irq_external_i, + input logic [14:0] irq_fast_i, + input logic nmi_mode_i, + output logic irq_pending_o, // interrupt request pending + output brq_pkg::irqs_t irqs_o, // interrupt requests qualified with mie + output logic csr_mstatus_mie_o, + output logic [31:0] csr_mepc_o, + + // PMP + output brq_pkg::pmp_cfg_t csr_pmp_cfg_o [PMPNumRegions], + output logic [33:0] csr_pmp_addr_o [PMPNumRegions], + + // debug + input logic debug_mode_i, + input brq_pkg::dbg_cause_e debug_cause_i, + input logic debug_csr_save_i, + output logic [31:0] csr_depc_o, + output logic debug_single_step_o, + output logic debug_ebreakm_o, + output logic debug_ebreaku_o, + output logic trigger_match_o, + + input logic [31:0] pc_if_i, + input logic [31:0] pc_id_i, + input logic [31:0] pc_wb_i, + + // CPU control bits + output logic data_ind_timing_o, + output logic dummy_instr_en_o, + output logic [2:0] dummy_instr_mask_o, + output logic dummy_instr_seed_en_o, + output logic [31:0] dummy_instr_seed_o, + output logic icache_enable_o, + output logic csr_shadow_err_o, + + // Exception save/restore + input logic csr_save_if_i, + input logic csr_save_id_i, + input logic csr_save_wb_i, + input logic csr_restore_mret_i, + input logic csr_restore_dret_i, + input logic csr_save_cause_i, + input brq_pkg::exc_cause_e csr_mcause_i, + input logic [31:0] csr_mtval_i, + output logic illegal_csr_insn_o, // access to non-existent CSR, + // with wrong priviledge level, or + // missing write permissions + // Performance Counters + input logic instr_ret_i, // instr retired in ID/EX stage + input logic instr_ret_compressed_i, // compressed instr retired + input logic iside_wait_i, // core waiting for the iside + input logic jump_i, // jump instr seen (j, jr, jal, jalr) + input logic branch_i, // branch instr seen (bf, bnf) + input logic branch_taken_i, // branch was taken + input logic mem_load_i, // load from memory in this cycle + input logic mem_store_i, // store to memory in this cycle + input logic dside_wait_i, // core waiting for the dside + input logic mul_wait_i, // core waiting for multiply + input logic div_wait_i, // core waiting for divide + + // floating point + input logic fp_rm_dynamic_i, + output fpnew_pkg::roundmode_e fp_frm_o, + input fpnew_pkg::status_t fp_status_i, + input logic is_fp_instr_i +); + import brq_pkg::*; + import fpnew_pkg::roundmode_e; + + localparam int unsigned RV32MEnabled = (RV32M == RV32MNone) ? 0 : 1; + localparam int unsigned PMPAddrWidth = (PMPGranularity > 0) ? 33 - PMPGranularity : 32; + + localparam int unsigned SinglePrecision = (RVF == RV32FSingle) ? 1 : 0; + localparam int unsigned DoublePrecision = (RVF == RV64FDouble) ? 1 : 0; + + // misa + localparam logic [31:0] MISA_VALUE = + (0 << 0) // A - Atomic Instructions extension + | (1 << 2) // C - Compressed extension + | (DoublePrecision << 3) // D - Double precision floating-point extension + | (32'(RV32E) << 4) // E - RV32E base ISA + | (SinglePrecision << 5) // F - Single precision floating-point extension + | (32'(!RV32E) << 8) // I - RV32I/64I/128I base ISA + | (RV32MEnabled << 12) // M - Integer Multiply/Divide extension + | (0 << 13) // N - User level interrupts supported + | (0 << 18) // S - Supervisor mode implemented + | (1 << 20) // U - User mode implemented + | (0 << 23) // X - Non-standard extensions present + | (32'(CSR_MISA_MXL) << 30); // M-XLEN + + typedef struct packed { + logic mie; + logic mpie; + priv_lvl_e mpp; + logic mprv; + logic tw; + } status_t; + + typedef struct packed { + logic mpie; + priv_lvl_e mpp; + } status_stk_t; + + typedef struct packed { + x_debug_ver_e xdebugver; + logic [11:0] zero2; + logic ebreakm; + logic zero1; + logic ebreaks; + logic ebreaku; + logic stepie; + logic stopcount; + logic stoptime; + dbg_cause_e cause; + logic zero0; + logic mprven; + logic nmip; + logic step; + priv_lvl_e prv; + } dcsr_t; + + // CPU control register fields + typedef struct packed { + logic [2:0] dummy_instr_mask; + logic dummy_instr_en; + logic data_ind_timing; + logic icache_enable; + } cpu_ctrl_t; + + // Interrupt and exception control signals + logic [31:0] exception_pc; + + // CSRs + fpnew_pkg::status_t fflags_q, fflags_d, fflag_wdata; + + logic fflags_en; + logic frm_en; + roundmode_e frm_q, frm_d; + priv_lvl_e priv_lvl_q, priv_lvl_d; + status_t mstatus_q, mstatus_d; + logic mstatus_err; + logic mstatus_en; + irqs_t mie_q, mie_d; + logic mie_en; + logic [31:0] mscratch_q; + logic mscratch_en; + logic [31:0] mepc_q, mepc_d; + logic mepc_en; + logic [5:0] mcause_q, mcause_d; + logic mcause_en; + logic [31:0] mtval_q, mtval_d; + logic mtval_en; + logic [31:0] mtvec_q, mtvec_d; + logic mtvec_err; + logic mtvec_en; + irqs_t mip; + dcsr_t dcsr_q, dcsr_d; + logic dcsr_en; + logic [31:0] depc_q, depc_d; + logic depc_en; + logic [31:0] dscratch0_q; + logic [31:0] dscratch1_q; + logic dscratch0_en, dscratch1_en; + + // CSRs for recoverable NMIs + // NOTE: these CSRS are nonstandard, see https://github.com/riscv/riscv-isa-manual/issues/261 + status_stk_t mstack_q, mstack_d; + logic mstack_en; + logic [31:0] mstack_epc_q, mstack_epc_d; + logic [5:0] mstack_cause_q, mstack_cause_d; + + // PMP Signals + logic [31:0] pmp_addr_rdata [PMP_MAX_REGIONS]; + logic [PMP_CFG_W-1:0] pmp_cfg_rdata [PMP_MAX_REGIONS]; + logic pmp_csr_err; + + // Hardware performance monitor signals + logic [31:0] mcountinhibit; + // Only have mcountinhibit flops for counters that actually exist + logic [MHPMCounterNum+3-1:0] mcountinhibit_d, mcountinhibit_q; + logic mcountinhibit_we; + + // mhpmcounter flops are elaborated below providing only the precise number that is required based + // on MHPMCounterNum/MHPMCounterWidth. This signal connects to the Q output of these flops + // where they exist and is otherwise 0. + logic [63:0] mhpmcounter [32]; + logic [31:0] mhpmcounter_we; + logic [31:0] mhpmcounterh_we; + logic [31:0] mhpmcounter_incr; + logic [31:0] mhpmevent [32]; + logic [4:0] mhpmcounter_idx; + logic unused_mhpmcounter_we_1; + logic unused_mhpmcounterh_we_1; + logic unused_mhpmcounter_incr_1; + + // Debug / trigger registers + logic [31:0] tselect_rdata; + logic [31:0] tmatch_control_rdata; + logic [31:0] tmatch_value_rdata; + + // CPU control bits + cpu_ctrl_t cpuctrl_q, cpuctrl_d, cpuctrl_wdata; + logic cpuctrl_we; + logic cpuctrl_err; + + // CSR update logic + logic [31:0] csr_wdata_int; + logic [31:0] csr_rdata_int; + logic csr_we_int; + logic csr_wreq; + + // Access violation signals + logic illegal_csr; + logic illegal_csr_priv; + logic illegal_csr_write; + + logic [7:0] unused_boot_addr; + logic [2:0] unused_csr_addr; + + assign unused_boot_addr = boot_addr_i[7:0]; + + ///////////// + // CSR reg // + ///////////// + + logic illegal_dyn_mod; + logic illegal_csr_dyn_mod; + logic [$bits(csr_num_e)-1:0] csr_addr; + assign csr_addr = {csr_addr_i}; + assign unused_csr_addr = csr_addr[7:5]; + assign mhpmcounter_idx = csr_addr[4:0]; + + assign illegal_csr_dyn_mod = illegal_dyn_mod & fp_rm_dynamic_i; + + // See RISC-V Privileged Specification, version 1.11, Section 2.1 + assign illegal_csr_priv = (csr_addr[9:8] > {priv_lvl_q}); + assign illegal_csr_write = (csr_addr[11:10] == 2'b11) && csr_wreq; + assign illegal_csr_insn_o = (csr_access_i & (illegal_csr | illegal_csr_write | illegal_csr_priv)) | illegal_csr_dyn_mod; + + // mip CSR is purely combinational - must be able to re-enable the clock upon WFI + assign mip.irq_software = irq_software_i; + assign mip.irq_timer = irq_timer_i; + assign mip.irq_external = irq_external_i; + assign mip.irq_fast = irq_fast_i; + + // Floating point + always_comb begin + unique case (frm_q) + 3'b000, + 3'b001, + 3'b010, + 3'b011, + 3'b100: illegal_dyn_mod = 1'b0; + 3'b101, + 3'b110, + 3'b111: illegal_dyn_mod = 1'b1; + endcase + fp_frm_o = frm_q; + end + + // read logic + always_comb begin + csr_rdata_int = '0; + illegal_csr = 1'b0; + + unique case (csr_addr_i) + // fcsr: floating-point control and status register (frm+fflags) + CSR_FCSR: csr_rdata_int = {24'b0 , frm_q, fflags_q}; + + // fflags: floating-point accrued exception + CSR_FFLAG: csr_rdata_int = {27'b0 , fflags_q}; + + // frm: floating-point dynamic rounding mode + CSR_FRM: begin + csr_rdata_int = {29'b0 , frm_q}; + end + + // mhartid: unique hardware thread id + CSR_MHARTID: csr_rdata_int = hart_id_i; + + // mstatus: always M-mode, contains IE bit + CSR_MSTATUS: begin + csr_rdata_int = '0; + csr_rdata_int[CSR_MSTATUS_MIE_BIT] = mstatus_q.mie; + csr_rdata_int[CSR_MSTATUS_MPIE_BIT] = mstatus_q.mpie; + csr_rdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW] = mstatus_q.mpp; + csr_rdata_int[CSR_MSTATUS_MPRV_BIT] = mstatus_q.mprv; + csr_rdata_int[CSR_MSTATUS_TW_BIT] = mstatus_q.tw; + end + + // misa + CSR_MISA: csr_rdata_int = MISA_VALUE; + + // interrupt enable + CSR_MIE: begin + csr_rdata_int = '0; + csr_rdata_int[CSR_MSIX_BIT] = mie_q.irq_software; + csr_rdata_int[CSR_MTIX_BIT] = mie_q.irq_timer; + csr_rdata_int[CSR_MEIX_BIT] = mie_q.irq_external; + csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mie_q.irq_fast; + end + + CSR_MSCRATCH: csr_rdata_int = mscratch_q; + + // mtvec: trap-vector base address + CSR_MTVEC: csr_rdata_int = mtvec_q; + + // mepc: exception program counter + CSR_MEPC: csr_rdata_int = mepc_q; + + // mcause: exception cause + CSR_MCAUSE: csr_rdata_int = {mcause_q[5], 26'b0, mcause_q[4:0]}; + + // mtval: trap value + CSR_MTVAL: csr_rdata_int = mtval_q; + + // mip: interrupt pending + CSR_MIP: begin + csr_rdata_int = '0; + csr_rdata_int[CSR_MSIX_BIT] = mip.irq_software; + csr_rdata_int[CSR_MTIX_BIT] = mip.irq_timer; + csr_rdata_int[CSR_MEIX_BIT] = mip.irq_external; + csr_rdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW] = mip.irq_fast; + end + + // PMP registers + CSR_PMPCFG0: csr_rdata_int = {pmp_cfg_rdata[3], pmp_cfg_rdata[2], + pmp_cfg_rdata[1], pmp_cfg_rdata[0]}; + CSR_PMPCFG1: csr_rdata_int = {pmp_cfg_rdata[7], pmp_cfg_rdata[6], + pmp_cfg_rdata[5], pmp_cfg_rdata[4]}; + CSR_PMPCFG2: csr_rdata_int = {pmp_cfg_rdata[11], pmp_cfg_rdata[10], + pmp_cfg_rdata[9], pmp_cfg_rdata[8]}; + CSR_PMPCFG3: csr_rdata_int = {pmp_cfg_rdata[15], pmp_cfg_rdata[14], + pmp_cfg_rdata[13], pmp_cfg_rdata[12]}; + CSR_PMPADDR0: csr_rdata_int = pmp_addr_rdata[0]; + CSR_PMPADDR1: csr_rdata_int = pmp_addr_rdata[1]; + CSR_PMPADDR2: csr_rdata_int = pmp_addr_rdata[2]; + CSR_PMPADDR3: csr_rdata_int = pmp_addr_rdata[3]; + CSR_PMPADDR4: csr_rdata_int = pmp_addr_rdata[4]; + CSR_PMPADDR5: csr_rdata_int = pmp_addr_rdata[5]; + CSR_PMPADDR6: csr_rdata_int = pmp_addr_rdata[6]; + CSR_PMPADDR7: csr_rdata_int = pmp_addr_rdata[7]; + CSR_PMPADDR8: csr_rdata_int = pmp_addr_rdata[8]; + CSR_PMPADDR9: csr_rdata_int = pmp_addr_rdata[9]; + CSR_PMPADDR10: csr_rdata_int = pmp_addr_rdata[10]; + CSR_PMPADDR11: csr_rdata_int = pmp_addr_rdata[11]; + CSR_PMPADDR12: csr_rdata_int = pmp_addr_rdata[12]; + CSR_PMPADDR13: csr_rdata_int = pmp_addr_rdata[13]; + CSR_PMPADDR14: csr_rdata_int = pmp_addr_rdata[14]; + CSR_PMPADDR15: csr_rdata_int = pmp_addr_rdata[15]; + + CSR_DCSR: begin + csr_rdata_int = dcsr_q; + illegal_csr = ~debug_mode_i; + end + CSR_DPC: begin + csr_rdata_int = depc_q; + illegal_csr = ~debug_mode_i; + end + CSR_DSCRATCH0: begin + csr_rdata_int = dscratch0_q; + illegal_csr = ~debug_mode_i; + end + CSR_DSCRATCH1: begin + csr_rdata_int = dscratch1_q; + illegal_csr = ~debug_mode_i; + end + + // machine counter/timers + CSR_MCOUNTINHIBIT: csr_rdata_int = mcountinhibit; + CSR_MHPMEVENT3, + CSR_MHPMEVENT4, CSR_MHPMEVENT5, CSR_MHPMEVENT6, CSR_MHPMEVENT7, + CSR_MHPMEVENT8, CSR_MHPMEVENT9, CSR_MHPMEVENT10, CSR_MHPMEVENT11, + CSR_MHPMEVENT12, CSR_MHPMEVENT13, CSR_MHPMEVENT14, CSR_MHPMEVENT15, + CSR_MHPMEVENT16, CSR_MHPMEVENT17, CSR_MHPMEVENT18, CSR_MHPMEVENT19, + CSR_MHPMEVENT20, CSR_MHPMEVENT21, CSR_MHPMEVENT22, CSR_MHPMEVENT23, + CSR_MHPMEVENT24, CSR_MHPMEVENT25, CSR_MHPMEVENT26, CSR_MHPMEVENT27, + CSR_MHPMEVENT28, CSR_MHPMEVENT29, CSR_MHPMEVENT30, CSR_MHPMEVENT31: begin + csr_rdata_int = mhpmevent[mhpmcounter_idx]; + end + + CSR_MCYCLE, + CSR_MINSTRET, + CSR_MHPMCOUNTER3, + CSR_MHPMCOUNTER4, CSR_MHPMCOUNTER5, CSR_MHPMCOUNTER6, CSR_MHPMCOUNTER7, + CSR_MHPMCOUNTER8, CSR_MHPMCOUNTER9, CSR_MHPMCOUNTER10, CSR_MHPMCOUNTER11, + CSR_MHPMCOUNTER12, CSR_MHPMCOUNTER13, CSR_MHPMCOUNTER14, CSR_MHPMCOUNTER15, + CSR_MHPMCOUNTER16, CSR_MHPMCOUNTER17, CSR_MHPMCOUNTER18, CSR_MHPMCOUNTER19, + CSR_MHPMCOUNTER20, CSR_MHPMCOUNTER21, CSR_MHPMCOUNTER22, CSR_MHPMCOUNTER23, + CSR_MHPMCOUNTER24, CSR_MHPMCOUNTER25, CSR_MHPMCOUNTER26, CSR_MHPMCOUNTER27, + CSR_MHPMCOUNTER28, CSR_MHPMCOUNTER29, CSR_MHPMCOUNTER30, CSR_MHPMCOUNTER31: begin + csr_rdata_int = mhpmcounter[mhpmcounter_idx][31:0]; + end + + CSR_MCYCLEH, + CSR_MINSTRETH, + CSR_MHPMCOUNTER3H, + CSR_MHPMCOUNTER4H, CSR_MHPMCOUNTER5H, CSR_MHPMCOUNTER6H, CSR_MHPMCOUNTER7H, + CSR_MHPMCOUNTER8H, CSR_MHPMCOUNTER9H, CSR_MHPMCOUNTER10H, CSR_MHPMCOUNTER11H, + CSR_MHPMCOUNTER12H, CSR_MHPMCOUNTER13H, CSR_MHPMCOUNTER14H, CSR_MHPMCOUNTER15H, + CSR_MHPMCOUNTER16H, CSR_MHPMCOUNTER17H, CSR_MHPMCOUNTER18H, CSR_MHPMCOUNTER19H, + CSR_MHPMCOUNTER20H, CSR_MHPMCOUNTER21H, CSR_MHPMCOUNTER22H, CSR_MHPMCOUNTER23H, + CSR_MHPMCOUNTER24H, CSR_MHPMCOUNTER25H, CSR_MHPMCOUNTER26H, CSR_MHPMCOUNTER27H, + CSR_MHPMCOUNTER28H, CSR_MHPMCOUNTER29H, CSR_MHPMCOUNTER30H, CSR_MHPMCOUNTER31H: begin + csr_rdata_int = mhpmcounter[mhpmcounter_idx][63:32]; + end + + // Debug triggers + CSR_TSELECT: begin + csr_rdata_int = tselect_rdata; + illegal_csr = ~DbgTriggerEn; + end + CSR_TDATA1: begin + csr_rdata_int = tmatch_control_rdata; + illegal_csr = ~DbgTriggerEn; + end + CSR_TDATA2: begin + csr_rdata_int = tmatch_value_rdata; + illegal_csr = ~DbgTriggerEn; + end + CSR_TDATA3: begin + csr_rdata_int = '0; + illegal_csr = ~DbgTriggerEn; + end + CSR_MCONTEXT: begin + csr_rdata_int = '0; + illegal_csr = ~DbgTriggerEn; + end + CSR_SCONTEXT: begin + csr_rdata_int = '0; + illegal_csr = ~DbgTriggerEn; + end + + // Custom CSR for controlling CPU features + CSR_CPUCTRL: begin + csr_rdata_int = {{32-$bits(cpu_ctrl_t){1'b0}},cpuctrl_q}; + end + + // Custom CSR for LFSR re-seeding (cannot be read) + CSR_SECURESEED: begin + csr_rdata_int = '0; + end + + default: begin + illegal_csr = 1'b1; + end + endcase + end + + // write logic + always_comb begin + exception_pc = pc_id_i; + + // Floating point + fflags_d = fflags_q; + fflags_en = 1'b0; + + frm_d = frm_q; + frm_en = 1'b0; + + priv_lvl_d = priv_lvl_q; + mstatus_en = 1'b0; + mstatus_d = mstatus_q; + mie_en = 1'b0; + mscratch_en = 1'b0; + mepc_en = 1'b0; + mepc_d = {csr_wdata_int[31:1], 1'b0}; + mcause_en = 1'b0; + mcause_d = {csr_wdata_int[31], csr_wdata_int[4:0]}; + mtval_en = 1'b0; + mtval_d = csr_wdata_int; + mtvec_en = csr_mtvec_init_i; + // mtvec.MODE set to vectored + // mtvec.BASE must be 256-byte aligned + mtvec_d = csr_mtvec_init_i ? {boot_addr_i[31:2], 2'b00} : + {csr_wdata_int[31:2], 2'b00}; + dcsr_en = 1'b0; + dcsr_d = dcsr_q; + depc_d = {csr_wdata_int[31:1], 1'b0}; + depc_en = 1'b0; + dscratch0_en = 1'b0; + dscratch1_en = 1'b0; + + mstack_en = 1'b0; + mstack_d.mpie = mstatus_q.mpie; + mstack_d.mpp = mstatus_q.mpp; + mstack_epc_d = mepc_q; + mstack_cause_d = mcause_q; + + mcountinhibit_we = 1'b0; + mhpmcounter_we = '0; + mhpmcounterh_we = '0; + + cpuctrl_we = 1'b0; + + if (csr_we_int) begin + unique case (csr_addr_i) + // mstatus: IE bit + + CSR_FCSR: begin + fflags_en = 1'b1; + frm_en = 1'b1; + fflags_d = csr_wdata_int[4:0]; + frm_d = roundmode_e'(csr_wdata_int[7:5]); + end + + + CSR_FFLAG : begin + fflags_en = 1'b1; + fflags_d = fpnew_pkg::status_t'(csr_wdata_int[4:0]); + end + + CSR_FRM: begin + frm_en = 1'b1; + frm_d = roundmode_e'(csr_wdata_int[2:0]); + end + + CSR_MSTATUS: begin + mstatus_en = 1'b1; + mstatus_d = '{ + mie: csr_wdata_int[CSR_MSTATUS_MIE_BIT], + mpie: csr_wdata_int[CSR_MSTATUS_MPIE_BIT], + mpp: priv_lvl_e'(csr_wdata_int[CSR_MSTATUS_MPP_BIT_HIGH:CSR_MSTATUS_MPP_BIT_LOW]), + mprv: csr_wdata_int[CSR_MSTATUS_MPRV_BIT], + tw: csr_wdata_int[CSR_MSTATUS_TW_BIT] + }; + // Convert illegal values to M-mode + if ((mstatus_d.mpp != PRIV_LVL_M) && (mstatus_d.mpp != PRIV_LVL_U)) begin + mstatus_d.mpp = PRIV_LVL_M; + end + end + + // interrupt enable + CSR_MIE: mie_en = 1'b1; + + CSR_MSCRATCH: mscratch_en = 1'b1; + + // mepc: exception program counter + CSR_MEPC: mepc_en = 1'b1; + + // mcause + CSR_MCAUSE: mcause_en = 1'b1; + + // mtval: trap value + CSR_MTVAL: mtval_en = 1'b1; + + // mtvec + CSR_MTVEC: mtvec_en = 1'b1; + + CSR_DCSR: begin + dcsr_d = csr_wdata_int; + dcsr_d.xdebugver = XDEBUGVER_STD; + // Change to PRIV_LVL_M if software writes an unsupported value + if ((dcsr_d.prv != PRIV_LVL_M) && (dcsr_d.prv != PRIV_LVL_U)) begin + dcsr_d.prv = PRIV_LVL_M; + end + + // Read-only for SW + dcsr_d.cause = dcsr_q.cause; + + // currently not supported: + dcsr_d.nmip = 1'b0; + dcsr_d.mprven = 1'b0; + dcsr_d.stopcount = 1'b0; + dcsr_d.stoptime = 1'b0; + + // forced to be zero + dcsr_d.zero0 = 1'b0; + dcsr_d.zero1 = 1'b0; + dcsr_d.zero2 = 12'h0; + dcsr_en = 1'b1; + end + + // dpc: debug program counter + CSR_DPC: depc_en = 1'b1; + + CSR_DSCRATCH0: dscratch0_en = 1'b1; + CSR_DSCRATCH1: dscratch1_en = 1'b1; + + // machine counter/timers + CSR_MCOUNTINHIBIT: mcountinhibit_we = 1'b1; + + CSR_MCYCLE, + CSR_MINSTRET, + CSR_MHPMCOUNTER3, + CSR_MHPMCOUNTER4, CSR_MHPMCOUNTER5, CSR_MHPMCOUNTER6, CSR_MHPMCOUNTER7, + CSR_MHPMCOUNTER8, CSR_MHPMCOUNTER9, CSR_MHPMCOUNTER10, CSR_MHPMCOUNTER11, + CSR_MHPMCOUNTER12, CSR_MHPMCOUNTER13, CSR_MHPMCOUNTER14, CSR_MHPMCOUNTER15, + CSR_MHPMCOUNTER16, CSR_MHPMCOUNTER17, CSR_MHPMCOUNTER18, CSR_MHPMCOUNTER19, + CSR_MHPMCOUNTER20, CSR_MHPMCOUNTER21, CSR_MHPMCOUNTER22, CSR_MHPMCOUNTER23, + CSR_MHPMCOUNTER24, CSR_MHPMCOUNTER25, CSR_MHPMCOUNTER26, CSR_MHPMCOUNTER27, + CSR_MHPMCOUNTER28, CSR_MHPMCOUNTER29, CSR_MHPMCOUNTER30, CSR_MHPMCOUNTER31: begin + mhpmcounter_we[mhpmcounter_idx] = 1'b1; + end + + CSR_MCYCLEH, + CSR_MINSTRETH, + CSR_MHPMCOUNTER3H, + CSR_MHPMCOUNTER4H, CSR_MHPMCOUNTER5H, CSR_MHPMCOUNTER6H, CSR_MHPMCOUNTER7H, + CSR_MHPMCOUNTER8H, CSR_MHPMCOUNTER9H, CSR_MHPMCOUNTER10H, CSR_MHPMCOUNTER11H, + CSR_MHPMCOUNTER12H, CSR_MHPMCOUNTER13H, CSR_MHPMCOUNTER14H, CSR_MHPMCOUNTER15H, + CSR_MHPMCOUNTER16H, CSR_MHPMCOUNTER17H, CSR_MHPMCOUNTER18H, CSR_MHPMCOUNTER19H, + CSR_MHPMCOUNTER20H, CSR_MHPMCOUNTER21H, CSR_MHPMCOUNTER22H, CSR_MHPMCOUNTER23H, + CSR_MHPMCOUNTER24H, CSR_MHPMCOUNTER25H, CSR_MHPMCOUNTER26H, CSR_MHPMCOUNTER27H, + CSR_MHPMCOUNTER28H, CSR_MHPMCOUNTER29H, CSR_MHPMCOUNTER30H, CSR_MHPMCOUNTER31H: begin + mhpmcounterh_we[mhpmcounter_idx] = 1'b1; + end + + CSR_CPUCTRL: cpuctrl_we = 1'b1; + + default:; + endcase + end + + // exception controller gets priority over other writes + unique case (1'b1) + + csr_save_cause_i: begin + unique case (1'b1) + csr_save_if_i: begin + exception_pc = pc_if_i; + end + csr_save_id_i: begin + exception_pc = pc_id_i; + end + csr_save_wb_i: begin + exception_pc = pc_wb_i; + end + default:; + endcase + + // Any exception, including debug mode, causes a switch to M-mode + priv_lvl_d = PRIV_LVL_M; + + if (debug_csr_save_i) begin + // all interrupts are masked + // do not update cause, epc, tval, epc and status + dcsr_d.prv = priv_lvl_q; + dcsr_d.cause = debug_cause_i; + dcsr_en = 1'b1; + depc_d = exception_pc; + depc_en = 1'b1; + end else if (!debug_mode_i) begin + // In debug mode, "exceptions do not update any registers. That + // includes cause, epc, tval, dpc and mstatus." [Debug Spec v0.13.2, p.39] + mtval_en = 1'b1; + mtval_d = csr_mtval_i; + mstatus_en = 1'b1; + mstatus_d.mie = 1'b0; // disable interrupts + // save current status + mstatus_d.mpie = mstatus_q.mie; + mstatus_d.mpp = priv_lvl_q; + mepc_en = 1'b1; + mepc_d = exception_pc; + mcause_en = 1'b1; + mcause_d = {csr_mcause_i}; + // save previous status for recoverable NMI + mstack_en = 1'b1; + end + end // csr_save_cause_i + + csr_restore_dret_i: begin // DRET + priv_lvl_d = dcsr_q.prv; + end // csr_restore_dret_i + + csr_restore_mret_i: begin // MRET + priv_lvl_d = mstatus_q.mpp; + mstatus_en = 1'b1; + mstatus_d.mie = mstatus_q.mpie; // re-enable interrupts + + if (nmi_mode_i) begin + // when returning from an NMI restore state from mstack CSR + mstatus_d.mpie = mstack_q.mpie; + mstatus_d.mpp = mstack_q.mpp; + mepc_en = 1'b1; + mepc_d = mstack_epc_q; + mcause_en = 1'b1; + mcause_d = mstack_cause_q; + end else begin + // otherwise just set mstatus.MPIE/MPP + // See RISC-V Privileged Specification, version 1.11, Section 3.1.6.1 + mstatus_d.mpie = 1'b1; + mstatus_d.mpp = PRIV_LVL_U; + end + end // csr_restore_mret_i + + default:; + endcase + end + + // Update current priv level + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + priv_lvl_q <= PRIV_LVL_M; + end else begin + priv_lvl_q <= priv_lvl_d; + end + end + + // Send current priv level to the decoder + assign priv_mode_id_o = priv_lvl_q; + // New instruction fetches need to account for updates to priv_lvl_q this cycle + assign priv_mode_if_o = priv_lvl_d; + // Load/store instructions must factor in MPRV for PMP checking + assign priv_mode_lsu_o = mstatus_q.mprv ? mstatus_q.mpp : priv_lvl_q; + + // CSR operation logic + always_comb begin + unique case (csr_op_i) + CSR_OP_WRITE: csr_wdata_int = csr_wdata_i; + CSR_OP_SET: csr_wdata_int = csr_wdata_i | csr_rdata_o; + CSR_OP_CLEAR: csr_wdata_int = ~csr_wdata_i & csr_rdata_o; + CSR_OP_READ: csr_wdata_int = csr_wdata_i; + // default: csr_wdata_int = csr_wdata_i; + endcase + end + + assign csr_wreq = csr_op_en_i & + (csr_op_i inside {CSR_OP_WRITE, + CSR_OP_SET, + CSR_OP_CLEAR}); + + // only write CSRs during one clock cycle + assign csr_we_int = csr_wreq & ~illegal_csr_insn_o; + + assign csr_rdata_o = csr_rdata_int; + + // directly output some registers + assign csr_mepc_o = mepc_q; + assign csr_depc_o = depc_q; + assign csr_mtvec_o = mtvec_q; + + assign csr_mstatus_mie_o = mstatus_q.mie; + assign csr_mstatus_tw_o = mstatus_q.tw; + assign debug_single_step_o = dcsr_q.step; + assign debug_ebreakm_o = dcsr_q.ebreakm; + assign debug_ebreaku_o = dcsr_q.ebreaku; + + // Qualify incoming interrupt requests in mip CSR with mie CSR for controller and to re-enable + // clock upon WFI (must be purely combinational). + assign irqs_o = mip & mie_q; + assign irq_pending_o = |irqs_o; + + //////////////////////// + // CSR instantiations // + //////////////////////// + + // MSTATUS + localparam status_t MSTATUS_RST_VAL = '{mie: 1'b0, + mpie: 1'b1, + mpp: PRIV_LVL_U, + mprv: 1'b0, + tw: 1'b0}; + brq_csr #( + .Width ($bits(status_t)), + .ShadowCopy (ShadowCSR), + .ResetValue ({MSTATUS_RST_VAL}) + ) u_mstatus_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i ({mstatus_d}), + .wr_en_i (mstatus_en), + .rd_data_o (mstatus_q), + .rd_error_o (mstatus_err) + ); + + assign fflag_wdata = is_fp_instr_i ? fp_status_i : fflags_d; + // FFLAGS + brq_csr #( + .Width (5), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) fflags_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (fflag_wdata), + .wr_en_i (fflags_en | is_fp_instr_i), + .rd_data_o (fflags_q), + .rd_error_o () + ); + + // FRM + brq_csr #( + .Width (3), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) frm_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (frm_d), + .wr_en_i (frm_en), + .rd_data_o (frm_q), + .rd_error_o () + ); + + // MEPC + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mepc_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mepc_d), + .wr_en_i (mepc_en), + .rd_data_o (mepc_q), + .rd_error_o () + ); + + // MIE + assign mie_d.irq_software = csr_wdata_int[CSR_MSIX_BIT]; + assign mie_d.irq_timer = csr_wdata_int[CSR_MTIX_BIT]; + assign mie_d.irq_external = csr_wdata_int[CSR_MEIX_BIT]; + assign mie_d.irq_fast = csr_wdata_int[CSR_MFIX_BIT_HIGH:CSR_MFIX_BIT_LOW]; + brq_csr #( + .Width ($bits(irqs_t)), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mie_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i ({mie_d}), + .wr_en_i (mie_en), + .rd_data_o (mie_q), + .rd_error_o () + ); + + // MSCRATCH + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mscratch_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (csr_wdata_int), + .wr_en_i (mscratch_en), + .rd_data_o (mscratch_q), + .rd_error_o () + ); + + // MCAUSE + brq_csr #( + .Width (6), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mcause_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mcause_d), + .wr_en_i (mcause_en), + .rd_data_o (mcause_q), + .rd_error_o () + ); + + // MTVAL + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mtval_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mtval_d), + .wr_en_i (mtval_en), + .rd_data_o (mtval_q), + .rd_error_o () + ); + + // MTVEC + brq_csr #( + .Width (32), + .ShadowCopy (ShadowCSR), + .ResetValue (32'd1) + ) u_mtvec_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mtvec_d), + .wr_en_i (mtvec_en), + .rd_data_o (mtvec_q), + .rd_error_o (mtvec_err) + ); + + // DCSR + localparam dcsr_t DCSR_RESET_VAL = '{ + xdebugver: XDEBUGVER_STD, + cause: DBG_CAUSE_NONE, // 3'h0 + prv: PRIV_LVL_M, + default: '0 + }; + brq_csr #( + .Width ($bits(dcsr_t)), + .ShadowCopy (1'b0), + .ResetValue ({DCSR_RESET_VAL}) + ) u_dcsr_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i ({dcsr_d}), + .wr_en_i (dcsr_en), + .rd_data_o (dcsr_q), + .rd_error_o () + ); + + // DEPC + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_depc_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (depc_d), + .wr_en_i (depc_en), + .rd_data_o (depc_q), + .rd_error_o () + ); + + // DSCRATCH0 + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_dscratch0_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (csr_wdata_int), + .wr_en_i (dscratch0_en), + .rd_data_o (dscratch0_q), + .rd_error_o () + ); + + // DSCRATCH1 + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_dscratch1_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (csr_wdata_int), + .wr_en_i (dscratch1_en), + .rd_data_o (dscratch1_q), + .rd_error_o () + ); + + // MSTACK + localparam status_stk_t MSTACK_RESET_VAL = '{ + mpie: 1'b1, + mpp: PRIV_LVL_U + }; + brq_csr #( + .Width ($bits(status_stk_t)), + .ShadowCopy (1'b0), + .ResetValue ({MSTACK_RESET_VAL}) + ) u_mstack_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i ({mstack_d}), + .wr_en_i (mstack_en), + .rd_data_o (mstack_q), + .rd_error_o () + ); + + // MSTACK_EPC + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mstack_epc_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mstack_epc_d), + .wr_en_i (mstack_en), + .rd_data_o (mstack_epc_q), + .rd_error_o () + ); + + // MSTACK_CAUSE + brq_csr #( + .Width (6), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_mstack_cause_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (mstack_cause_d), + .wr_en_i (mstack_en), + .rd_data_o (mstack_cause_q), + .rd_error_o () + ); + + // ----------------- + // PMP registers + // ----------------- + + if (PMPEnable) begin : g_pmp_registers + pmp_cfg_t pmp_cfg [PMPNumRegions]; + pmp_cfg_t pmp_cfg_wdata [PMPNumRegions]; + logic [PMPAddrWidth-1:0] pmp_addr [PMPNumRegions]; + logic [PMPNumRegions-1:0] pmp_cfg_we; + logic [PMPNumRegions-1:0] pmp_cfg_err; + logic [PMPNumRegions-1:0] pmp_addr_we; + logic [PMPNumRegions-1:0] pmp_addr_err; + + // Expanded / qualified register read data + for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_exp_rd_data + if (i < PMPNumRegions) begin : g_implemented_regions + // Add in zero padding for reserved fields + assign pmp_cfg_rdata[i] = {pmp_cfg[i].lock, 2'b00, pmp_cfg[i].mode, + pmp_cfg[i].exec, pmp_cfg[i].write, pmp_cfg[i].read}; + + // Address field read data depends on the current programmed mode and the granularity + // See RISC-V Privileged Specification, version 1.11, Section 3.6.1 + if (PMPGranularity == 0) begin : g_pmp_g0 + // If G == 0, read data is unmodified + assign pmp_addr_rdata[i] = pmp_addr[i]; + + end else if (PMPGranularity == 1) begin : g_pmp_g1 + // If G == 1, bit [G-1] reads as zero in TOR or OFF mode + always_comb begin + pmp_addr_rdata[i] = pmp_addr[i]; + if ((pmp_cfg[i].mode == PMP_MODE_OFF) || (pmp_cfg[i].mode == PMP_MODE_TOR)) begin + pmp_addr_rdata[i][PMPGranularity-1:0] = '0; + end + end + + end else begin : g_pmp_g2 + // For G >= 2, bits are masked to one or zero depending on the mode + always_comb begin + // In NAPOT mode, bits [G-2:0] must read as one + pmp_addr_rdata[i] = {pmp_addr[i], {PMPGranularity-1{1'b1}}}; + + if ((pmp_cfg[i].mode == PMP_MODE_OFF) || (pmp_cfg[i].mode == PMP_MODE_TOR)) begin + // In TOR or OFF mode, bits [G-1:0] must read as zero + pmp_addr_rdata[i][PMPGranularity-1:0] = '0; + end + end + end + + end else begin : g_other_regions + // Non-implemented regions read as zero + assign pmp_cfg_rdata[i] = '0; + assign pmp_addr_rdata[i] = '0; + end + end + + // Write data calculation + for (genvar i = 0; i < PMPNumRegions; i++) begin : g_pmp_csrs + // ------------------------- + // Instantiate cfg registers + // ------------------------- + assign pmp_cfg_we[i] = csr_we_int & ~pmp_cfg[i].lock & + (csr_addr == (CSR_OFF_PMP_CFG + (i[11:0] >> 2))); + + // Select the correct WDATA (each CSR contains 4 CFG fields, each with 2 RES bits) + assign pmp_cfg_wdata[i].lock = csr_wdata_int[(i%4)*PMP_CFG_W+7]; + // NA4 mode is not selectable when G > 0, mode is treated as OFF + always_comb begin + unique case (csr_wdata_int[(i%4)*PMP_CFG_W+3+:2]) + 2'b00 : pmp_cfg_wdata[i].mode = PMP_MODE_OFF; + 2'b01 : pmp_cfg_wdata[i].mode = PMP_MODE_TOR; + 2'b10 : pmp_cfg_wdata[i].mode = (PMPGranularity == 0) ? PMP_MODE_NA4: + PMP_MODE_OFF; + 2'b11 : pmp_cfg_wdata[i].mode = PMP_MODE_NAPOT; + default : pmp_cfg_wdata[i].mode = PMP_MODE_OFF; + endcase + end + assign pmp_cfg_wdata[i].exec = csr_wdata_int[(i%4)*PMP_CFG_W+2]; + // W = 1, R = 0 is a reserved combination. For now, we force W to 0 if R == 0 + assign pmp_cfg_wdata[i].write = &csr_wdata_int[(i%4)*PMP_CFG_W+:2]; + assign pmp_cfg_wdata[i].read = csr_wdata_int[(i%4)*PMP_CFG_W]; + + brq_csr #( + .Width ($bits(pmp_cfg_t)), + .ShadowCopy (ShadowCSR), + .ResetValue ('0) + ) u_pmp_cfg_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i ({pmp_cfg_wdata[i]}), + .wr_en_i (pmp_cfg_we[i]), + .rd_data_o (pmp_cfg[i]), + .rd_error_o (pmp_cfg_err[i]) + ); + + // -------------------------- + // Instantiate addr registers + // -------------------------- + if (i < PMPNumRegions - 1) begin : g_lower + assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock & + (~pmp_cfg[i+1].lock | (pmp_cfg[i+1].mode != PMP_MODE_TOR)) & + (csr_addr == (CSR_OFF_PMP_ADDR + i[11:0])); + end else begin : g_upper + assign pmp_addr_we[i] = csr_we_int & ~pmp_cfg[i].lock & + (csr_addr == (CSR_OFF_PMP_ADDR + i[11:0])); + end + + brq_csr #( + .Width (PMPAddrWidth), + .ShadowCopy (ShadowCSR), + .ResetValue ('0) + ) u_pmp_addr_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (csr_wdata_int[31-:PMPAddrWidth]), + .wr_en_i (pmp_addr_we[i]), + .rd_data_o (pmp_addr[i]), + .rd_error_o (pmp_addr_err[i]) + ); + + assign csr_pmp_cfg_o[i] = pmp_cfg[i]; + assign csr_pmp_addr_o[i] = {pmp_addr_rdata[i], 2'b00}; + end + + assign pmp_csr_err = (|pmp_cfg_err) | (|pmp_addr_err); + + end else begin : g_no_pmp_tieoffs + // Generate tieoffs when PMP is not configured + for (genvar i = 0; i < PMP_MAX_REGIONS; i++) begin : g_rdata + assign pmp_addr_rdata[i] = '0; + assign pmp_cfg_rdata[i] = '0; + end + for (genvar i = 0; i < PMPNumRegions; i++) begin : g_outputs + assign csr_pmp_cfg_o[i] = pmp_cfg_t'(1'b0); + assign csr_pmp_addr_o[i] = '0; + end + assign pmp_csr_err = 1'b0; + end + + ////////////////////////// + // Performance monitor // + ////////////////////////// + + // update enable signals + always_comb begin : mcountinhibit_update + if (mcountinhibit_we == 1'b1) begin + // bit 1 must always be 0 + mcountinhibit_d = {csr_wdata_int[MHPMCounterNum+2:2], 1'b0, csr_wdata_int[0]}; + end else begin + mcountinhibit_d = mcountinhibit_q; + end + end + + // event selection (hardwired) & control + always_comb begin : gen_mhpmcounter_incr + + // Assign inactive counters (first to prevent latch inference) + for (int unsigned i=0; i<32; i++) begin : gen_mhpmcounter_incr_inactive + mhpmcounter_incr[i] = 1'b0; + end + + // When adding or altering performance counter meanings and default + // mappings please update dv/verilator/pcount/cpp/brq_pcounts.cc + // appropriately. + // + // active counters + mhpmcounter_incr[0] = 1'b1; // mcycle + mhpmcounter_incr[1] = 1'b0; // reserved + mhpmcounter_incr[2] = instr_ret_i; // minstret + mhpmcounter_incr[3] = dside_wait_i; // cycles waiting for data memory + mhpmcounter_incr[4] = iside_wait_i; // cycles waiting for instr fetches + mhpmcounter_incr[5] = mem_load_i; // num of loads + mhpmcounter_incr[6] = mem_store_i; // num of stores + mhpmcounter_incr[7] = jump_i; // num of jumps (unconditional) + mhpmcounter_incr[8] = branch_i; // num of branches (conditional) + mhpmcounter_incr[9] = branch_taken_i; // num of taken branches (conditional) + mhpmcounter_incr[10] = instr_ret_compressed_i; // num of compressed instr + mhpmcounter_incr[11] = mul_wait_i; // cycles waiting for multiply + mhpmcounter_incr[12] = div_wait_i; // cycles waiting for divide + end + + // event selector (hardwired, 0 means no event) + always_comb begin : gen_mhpmevent + + // activate all + for (int i=0; i<32; i++) begin : gen_mhpmevent_active + mhpmevent[i] = '0; + mhpmevent[i][i] = 1'b1; + end + + // deactivate + mhpmevent[1] = '0; // not existing, reserved + for (int unsigned i=3+MHPMCounterNum; i<32; i++) begin : gen_mhpmevent_inactive + mhpmevent[i] = '0; + end + end + + // mcycle + brq_counter #( + .CounterWidth(64) + ) mcycle_counter_i ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .counter_inc_i(mhpmcounter_incr[0] & ~mcountinhibit[0]), + .counterh_we_i(mhpmcounterh_we[0]), + .counter_we_i(mhpmcounter_we[0]), + .counter_val_i(csr_wdata_int), + .counter_val_o(mhpmcounter[0]) + ); + + // minstret + brq_counter #( + .CounterWidth(64) + ) minstret_counter_i ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .counter_inc_i(mhpmcounter_incr[2] & ~mcountinhibit[2]), + .counterh_we_i(mhpmcounterh_we[2]), + .counter_we_i(mhpmcounter_we[2]), + .counter_val_i(csr_wdata_int), + .counter_val_o(mhpmcounter[2]) + ); + + // reserved: + assign mhpmcounter[1] = '0; + assign unused_mhpmcounter_we_1 = mhpmcounter_we[1]; + assign unused_mhpmcounterh_we_1 = mhpmcounterh_we[1]; + assign unused_mhpmcounter_incr_1 = mhpmcounter_incr[1]; + + for (genvar cnt=0; cnt < 29; cnt++) begin : gen_cntrs + if (cnt < MHPMCounterNum) begin : gen_imp + brq_counter #( + .CounterWidth(MHPMCounterWidth) + ) mcounters_variable_i ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .counter_inc_i(mhpmcounter_incr[cnt+3] & ~mcountinhibit[cnt+3]), + .counterh_we_i(mhpmcounterh_we[cnt+3]), + .counter_we_i(mhpmcounter_we[cnt+3]), + .counter_val_i(csr_wdata_int), + .counter_val_o(mhpmcounter[cnt+3]) + ); + end else begin : gen_unimp + assign mhpmcounter[cnt+3] = '0; + end + end + + if(MHPMCounterNum < 29) begin : g_mcountinhibit_reduced + logic [29-MHPMCounterNum-1:0] unused_mhphcounter_we; + logic [29-MHPMCounterNum-1:0] unused_mhphcounterh_we; + logic [29-MHPMCounterNum-1:0] unused_mhphcounter_incr; + + assign mcountinhibit = {{29-MHPMCounterNum{1'b1}}, mcountinhibit_q}; + // Lint tieoffs for unused bits + assign unused_mhphcounter_we = mhpmcounter_we[31:MHPMCounterNum+3]; + assign unused_mhphcounterh_we = mhpmcounterh_we[31:MHPMCounterNum+3]; + assign unused_mhphcounter_incr = mhpmcounter_incr[31:MHPMCounterNum+3]; + end else begin : g_mcountinhibit_full + assign mcountinhibit = mcountinhibit_q; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + mcountinhibit_q <= '0; + end else begin + mcountinhibit_q <= mcountinhibit_d; + end + end + + ///////////////////////////// + // Debug trigger registers // + ///////////////////////////// + + if (DbgTriggerEn) begin : gen_trigger_regs + localparam int unsigned DbgHwNumLen = DbgHwBreakNum > 1 ? $clog2(DbgHwBreakNum) : 1; + // Register values + logic [DbgHwNumLen-1:0] tselect_d, tselect_q; + logic tmatch_control_d; + logic [DbgHwBreakNum-1:0] tmatch_control_q; + logic [31:0] tmatch_value_d; + logic [31:0] tmatch_value_q[DbgHwBreakNum]; + // Write enables + logic tselect_we; + logic [DbgHwBreakNum-1:0] tmatch_control_we; + logic [DbgHwBreakNum-1:0] tmatch_value_we; + // Trigger comparison result + logic [DbgHwBreakNum-1:0] trigger_match; + + // Write select + assign tselect_we = csr_we_int & debug_mode_i & (csr_addr_i == CSR_TSELECT); + for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_tmatch_we + assign tmatch_control_we[i] = (i[DbgHwNumLen-1:0] == tselect_q) & csr_we_int & debug_mode_i & + (csr_addr_i == CSR_TDATA1); + assign tmatch_value_we[i] = (i[DbgHwNumLen-1:0] == tselect_q) & csr_we_int & debug_mode_i & + (csr_addr_i == CSR_TDATA2); + end + + // Debug interface tests the available number of triggers by writing and reading the trigger + // select register. Only allow changes to the register if it is within the supported region. + assign tselect_d = (csr_wdata_int < DbgHwBreakNum) ? csr_wdata_int[DbgHwNumLen-1:0] : + DbgHwBreakNum-1; + // tmatch_control is enabled when the execute bit is set + assign tmatch_control_d = csr_wdata_int[2]; + assign tmatch_value_d = csr_wdata_int[31:0]; + + // Registers + brq_csr #( + .Width (DbgHwNumLen), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_tselect_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (tselect_d), + .wr_en_i (tselect_we), + .rd_data_o (tselect_q), + .rd_error_o () + ); + + for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_tmatch_reg + brq_csr #( + .Width (1), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_tmatch_control_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (tmatch_control_d), + .wr_en_i (tmatch_control_we[i]), + .rd_data_o (tmatch_control_q[i]), + .rd_error_o () + ); + + brq_csr #( + .Width (32), + .ShadowCopy (1'b0), + .ResetValue ('0) + ) u_tmatch_value_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i (tmatch_value_d), + .wr_en_i (tmatch_value_we[i]), + .rd_data_o (tmatch_value_q[i]), + .rd_error_o () + ); + end + + // Assign read data + // TSELECT - number of supported triggers defined by parameter DbgHwBreakNum + localparam int unsigned TSelectRdataPadlen = DbgHwNumLen >= 32 ? 0 : (32 - DbgHwNumLen); + assign tselect_rdata = {{TSelectRdataPadlen{1'b0}}, tselect_q}; + + // TDATA0 - only support simple address matching + assign tmatch_control_rdata = {4'h2, // type : address/data match + 1'b1, // dmode : access from D mode only + 6'h00, // maskmax : exact match only + 1'b0, // hit : not supported + 1'b0, // select : address match only + 1'b0, // timing : match before execution + 2'b00, // sizelo : match any access + 4'h1, // action : enter debug mode + 1'b0, // chain : not supported + 4'h0, // match : simple match + 1'b1, // m : match in m-mode + 1'b0, // 0 : zero + 1'b0, // s : not supported + 1'b1, // u : match in u-mode + tmatch_control_q[tselect_q], // execute : match instruction address + 1'b0, // store : not supported + 1'b0}; // load : not supported + // TDATA1 - address match value only + assign tmatch_value_rdata = tmatch_value_q[tselect_q]; + + // Breakpoint matching + // We match against the next address, as the breakpoint must be taken before execution + for (genvar i = 0; i < DbgHwBreakNum; i++) begin : g_dbg_trigger_match + assign trigger_match[i] = tmatch_control_q[i] & (pc_if_i[31:0] == tmatch_value_q[i]); + end + assign trigger_match_o = |trigger_match; + + end else begin : gen_no_trigger_regs + assign tselect_rdata = 'b0; + assign tmatch_control_rdata = 'b0; + assign tmatch_value_rdata = 'b0; + assign trigger_match_o = 'b0; + end + + ////////////////////////// + // CPU control register // + ////////////////////////// + + // Cast register write data + assign cpuctrl_wdata = cpu_ctrl_t'(csr_wdata_int[$bits(cpu_ctrl_t)-1:0]); + + // Generate fixed time execution bit + if (DataIndTiming) begin : gen_dit + assign cpuctrl_d.data_ind_timing = cpuctrl_wdata.data_ind_timing; + + end else begin : gen_no_dit + // tieoff for the unused bit + logic unused_dit; + assign unused_dit = cpuctrl_wdata.data_ind_timing; + + // field will always read as zero if not configured + assign cpuctrl_d.data_ind_timing = 1'b0; + end + + assign data_ind_timing_o = cpuctrl_q.data_ind_timing; + + // Generate dummy instruction signals + if (DummyInstructions) begin : gen_dummy + assign cpuctrl_d.dummy_instr_en = cpuctrl_wdata.dummy_instr_en; + assign cpuctrl_d.dummy_instr_mask = cpuctrl_wdata.dummy_instr_mask; + + // Signal a write to the seed register + assign dummy_instr_seed_en_o = csr_we_int && (csr_addr == CSR_SECURESEED); + assign dummy_instr_seed_o = csr_wdata_int; + + end else begin : gen_no_dummy + // tieoff for the unused bit + logic unused_dummy_en; + logic [2:0] unused_dummy_mask; + assign unused_dummy_en = cpuctrl_wdata.dummy_instr_en; + assign unused_dummy_mask = cpuctrl_wdata.dummy_instr_mask; + + // field will always read as zero if not configured + assign cpuctrl_d.dummy_instr_en = 1'b0; + assign cpuctrl_d.dummy_instr_mask = 3'b000; + assign dummy_instr_seed_en_o = 1'b0; + assign dummy_instr_seed_o = '0; + end + + assign dummy_instr_en_o = cpuctrl_q.dummy_instr_en; + assign dummy_instr_mask_o = cpuctrl_q.dummy_instr_mask; + + // Generate icache enable bit + if (ICache) begin : gen_icache_enable + assign cpuctrl_d.icache_enable = cpuctrl_wdata.icache_enable; + end else begin : gen_no_icache + // tieoff for the unused icen bit + logic unused_icen; + assign unused_icen = cpuctrl_wdata.icache_enable; + + // icen field will always read as zero if ICache not configured + assign cpuctrl_d.icache_enable = 1'b0; + end + + assign icache_enable_o = cpuctrl_q.icache_enable; + + brq_csr #( + .Width ($bits(cpu_ctrl_t)), + .ShadowCopy (ShadowCSR), + .ResetValue ('0) + ) u_cpuctrl_csr ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .wr_data_i ({cpuctrl_d}), + .wr_en_i (cpuctrl_we), + .rd_data_o (cpuctrl_q), + .rd_error_o (cpuctrl_err) + ); + + assign csr_shadow_err_o = mstatus_err | mtvec_err | pmp_csr_err | cpuctrl_err; + + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_csr.sv b/verilog/rtl/brq_csr.sv new file mode 100644 index 0000000..b1c4477 --- /dev/null +++ b/verilog/rtl/brq_csr.sv
@@ -0,0 +1,53 @@ + + +/** + * Control / status register primitive + */ + + +module brq_csr #( + parameter int unsigned Width = 32, + parameter bit ShadowCopy = 1'b0, + parameter bit [Width-1:0] ResetValue = '0 + ) ( + input logic clk_i, + input logic rst_ni, + + input logic [Width-1:0] wr_data_i, + input logic wr_en_i, + output logic [Width-1:0] rd_data_o, + + output logic rd_error_o +); + + logic [Width-1:0] rdata_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rdata_q <= ResetValue; + end else if (wr_en_i) begin + rdata_q <= wr_data_i; + end + end + + assign rd_data_o = rdata_q; + + if (ShadowCopy) begin : gen_shadow + logic [Width-1:0] shadow_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + shadow_q <= ~ResetValue; + end else if (wr_en_i) begin + shadow_q <= ~wr_data_i; + end + end + + assign rd_error_o = rdata_q != ~shadow_q; + + end else begin : gen_no_shadow + assign rd_error_o = 1'b0; + end + + +endmodule
diff --git a/verilog/rtl/brq_exu.sv b/verilog/rtl/brq_exu.sv new file mode 100644 index 0000000..883cf7a --- /dev/null +++ b/verilog/rtl/brq_exu.sv
@@ -0,0 +1,194 @@ +/** + * Execution stage + * + * Execution block: Hosts ALU and MUL/DIV unit + */ +module brq_exu #( + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, + parameter brq_pkg::rv32b_e RV32B = brq_pkg::RV32BNone, + parameter bit BranchTargetALU = 0 +) ( + input logic clk_i, + input logic rst_ni, + + // ALU + input brq_pkg::alu_op_e alu_operator_i, + input logic [31:0] alu_operand_a_i, + input logic [31:0] alu_operand_b_i, + input logic alu_instr_first_cycle_i, + + // Branch Target ALU + // All of these signals are unusued when BranchTargetALU == 0 + input logic [31:0] bt_a_operand_i, + input logic [31:0] bt_b_operand_i, + + // Multiplier/Divider + input brq_pkg::md_op_e multdiv_operator_i, + input logic mult_en_i, // dynamic enable signal, for FSM control + input logic div_en_i, // dynamic enable signal, for FSM control + input logic mult_sel_i, // static decoder output, for data muxes + input logic div_sel_i, // static decoder output, for data muxes + input logic [1:0] multdiv_signed_mode_i, + input logic [31:0] multdiv_operand_a_i, + input logic [31:0] multdiv_operand_b_i, + input logic multdiv_ready_id_i, + input logic data_ind_timing_i, + + // intermediate val reg + output logic [1:0] imd_val_we_o, + output logic [33:0] imd_val_d_o[2], + input logic [33:0] imd_val_q_i[2], + + // Outputs + output logic [31:0] alu_adder_result_ex_o, // to LSU + output logic [31:0] result_ex_o, + output logic [31:0] branch_target_o, // to IF + output logic branch_decision_o, // to ID + + output logic ex_valid_o // EX has valid output +); + + import brq_pkg::*; + + logic [31:0] alu_result, multdiv_result; + + logic [32:0] multdiv_alu_operand_b, multdiv_alu_operand_a; + logic [33:0] alu_adder_result_ext; + logic alu_cmp_result, alu_is_equal_result; + logic multdiv_valid; + logic multdiv_sel; + logic [31:0] alu_imd_val_q[2]; + logic [31:0] alu_imd_val_d[2]; + logic [ 1:0] alu_imd_val_we; + logic [33:0] multdiv_imd_val_d[2]; + logic [ 1:0] multdiv_imd_val_we; + + /* + The multdiv_i output is never selected if RV32M=RV32MNone + At synthesis time, all the combinational and sequential logic + from the multdiv_i module are eliminated + */ + if (RV32M != RV32MNone) begin : gen_multdiv_m + assign multdiv_sel = mult_sel_i | div_sel_i; + end else begin : gen_multdiv_no_m + assign multdiv_sel = 1'b0; + end + + // Intermediate Value Register Mux + assign imd_val_d_o[0] = multdiv_sel ? multdiv_imd_val_d[0] : {2'b0, alu_imd_val_d[0]}; + assign imd_val_d_o[1] = multdiv_sel ? multdiv_imd_val_d[1] : {2'b0, alu_imd_val_d[1]}; + assign imd_val_we_o = multdiv_sel ? multdiv_imd_val_we : alu_imd_val_we; + + assign alu_imd_val_q = '{imd_val_q_i[0][31:0], imd_val_q_i[1][31:0]}; + + assign result_ex_o = multdiv_sel ? multdiv_result : alu_result; + + // branch handling + assign branch_decision_o = alu_cmp_result; + + if (BranchTargetALU) begin : g_branch_target_alu + logic [32:0] bt_alu_result; + logic unused_bt_carry; + + assign bt_alu_result = bt_a_operand_i + bt_b_operand_i; + + assign unused_bt_carry = bt_alu_result[32]; + assign branch_target_o = bt_alu_result[31:0]; + end else begin : g_no_branch_target_alu + // Unused bt_operand signals cause lint errors, this avoids them + logic [31:0] unused_bt_a_operand, unused_bt_b_operand; + + assign unused_bt_a_operand = bt_a_operand_i; + assign unused_bt_b_operand = bt_b_operand_i; + + assign branch_target_o = alu_adder_result_ex_o; + end + + ///////// + // ALU // + ///////// + + brq_exu_alu #( + .RV32B(RV32B) + ) alu_i ( + .operator_i ( alu_operator_i ), + .operand_a_i ( alu_operand_a_i ), + .operand_b_i ( alu_operand_b_i ), + .instr_first_cycle_i ( alu_instr_first_cycle_i ), + .imd_val_q_i ( alu_imd_val_q ), + .imd_val_we_o ( alu_imd_val_we ), + .imd_val_d_o ( alu_imd_val_d ), + .multdiv_operand_a_i ( multdiv_alu_operand_a ), + .multdiv_operand_b_i ( multdiv_alu_operand_b ), + .multdiv_sel_i ( multdiv_sel ), + .adder_result_o ( alu_adder_result_ex_o ), + .adder_result_ext_o ( alu_adder_result_ext ), + .result_o ( alu_result ), + .comparison_result_o ( alu_cmp_result ), + .is_equal_result_o ( alu_is_equal_result ) + ); + + //////////////// + // Multiplier // + //////////////// + + if (RV32M == RV32MSlow) begin : gen_multdiv_slow + brq_exu_multdiv_slow multdiv_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .mult_en_i ( mult_en_i ), + .div_en_i ( div_en_i ), + .mult_sel_i ( mult_sel_i ), + .div_sel_i ( div_sel_i ), + .operator_i ( multdiv_operator_i ), + .signed_mode_i ( multdiv_signed_mode_i ), + .op_a_i ( multdiv_operand_a_i ), + .op_b_i ( multdiv_operand_b_i ), + .alu_adder_ext_i ( alu_adder_result_ext ), + .alu_adder_i ( alu_adder_result_ex_o ), + .equal_to_zero_i ( alu_is_equal_result ), + .data_ind_timing_i ( data_ind_timing_i ), + .valid_o ( multdiv_valid ), + .alu_operand_a_o ( multdiv_alu_operand_a ), + .alu_operand_b_o ( multdiv_alu_operand_b ), + .imd_val_q_i ( imd_val_q_i ), + .imd_val_d_o ( multdiv_imd_val_d ), + .imd_val_we_o ( multdiv_imd_val_we ), + .multdiv_ready_id_i ( multdiv_ready_id_i ), + .multdiv_result_o ( multdiv_result ) + ); + end else if (RV32M == RV32MFast || RV32M == RV32MSingleCycle) begin : gen_multdiv_fast + brq_exu_multdiv_fast # ( + .RV32M ( RV32M ) + ) multdiv_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .mult_en_i ( mult_en_i ), + .div_en_i ( div_en_i ), + .mult_sel_i ( mult_sel_i ), + .div_sel_i ( div_sel_i ), + .operator_i ( multdiv_operator_i ), + .signed_mode_i ( multdiv_signed_mode_i ), + .op_a_i ( multdiv_operand_a_i ), + .op_b_i ( multdiv_operand_b_i ), + .alu_operand_a_o ( multdiv_alu_operand_a ), + .alu_operand_b_o ( multdiv_alu_operand_b ), + .alu_adder_ext_i ( alu_adder_result_ext ), + .alu_adder_i ( alu_adder_result_ex_o ), + .equal_to_zero_i ( alu_is_equal_result ), + .data_ind_timing_i ( data_ind_timing_i ), + .imd_val_q_i ( imd_val_q_i ), + .imd_val_d_o ( multdiv_imd_val_d ), + .imd_val_we_o ( multdiv_imd_val_we ), + .multdiv_ready_id_i ( multdiv_ready_id_i ), + .valid_o ( multdiv_valid ), + .multdiv_result_o ( multdiv_result ) + ); + end + + // Multiplier/divider may require multiple cycles. The ALU output is valid in the same cycle + // unless the intermediate result register is being written (which indicates this isn't the + // final cycle of ALU operation). + assign ex_valid_o = multdiv_sel ? multdiv_valid : ~(|alu_imd_val_we); + +endmodule
diff --git a/verilog/rtl/brq_exu_alu.sv b/verilog/rtl/brq_exu_alu.sv new file mode 100644 index 0000000..99fe60a --- /dev/null +++ b/verilog/rtl/brq_exu_alu.sv
@@ -0,0 +1,1267 @@ + +/** + * Arithmetic logic unit + */ +module brq_exu_alu #( + parameter brq_pkg::rv32b_e RV32B = brq_pkg::RV32BNone +) ( + input brq_pkg::alu_op_e operator_i, + input logic [31:0] operand_a_i, + input logic [31:0] operand_b_i, + + input logic instr_first_cycle_i, + + input logic [32:0] multdiv_operand_a_i, + input logic [32:0] multdiv_operand_b_i, + + input logic multdiv_sel_i, + + input logic [31:0] imd_val_q_i[2], + output logic [31:0] imd_val_d_o[2], + output logic [1:0] imd_val_we_o, + + output logic [31:0] adder_result_o, + output logic [33:0] adder_result_ext_o, + + output logic [31:0] result_o, + output logic comparison_result_o, + output logic is_equal_result_o +); + import brq_pkg::*; + + logic [31:0] operand_a_rev; + logic [32:0] operand_b_neg; + + // bit reverse operand_a for left shifts and bit counting + for (genvar k = 0; k < 32; k++) begin : gen_rev_operand_a + assign operand_a_rev[k] = operand_a_i[31-k]; + end + + /////////// + // Adder // + /////////// + + logic adder_op_b_negate; + logic [32:0] adder_in_a, adder_in_b; + logic [31:0] adder_result; + + always_comb begin + adder_op_b_negate = 1'b0; + unique case (operator_i) + // Adder OPs + ALU_SUB, + + // Comparator OPs + ALU_EQ, ALU_NE, + ALU_GE, ALU_GEU, + ALU_LT, ALU_LTU, + ALU_SLT, ALU_SLTU, + + // MinMax OPs (RV32B Ops) + ALU_MIN, ALU_MINU, + ALU_MAX, ALU_MAXU: adder_op_b_negate = 1'b1; + + default:; + endcase + end + + // prepare operand a + assign adder_in_a = multdiv_sel_i ? multdiv_operand_a_i : {operand_a_i,1'b1}; + + // prepare operand b + assign operand_b_neg = {operand_b_i,1'b0} ^ {33{1'b1}}; + always_comb begin + unique case(1'b1) + multdiv_sel_i: adder_in_b = multdiv_operand_b_i; + adder_op_b_negate: adder_in_b = operand_b_neg; + default : adder_in_b = {operand_b_i, 1'b0}; + endcase + end + + // actual adder + assign adder_result_ext_o = $unsigned(adder_in_a) + $unsigned(adder_in_b); + + assign adder_result = adder_result_ext_o[32:1]; + + assign adder_result_o = adder_result; + + //////////////// + // Comparison // + //////////////// + + logic is_equal; + logic is_greater_equal; // handles both signed and unsigned forms + logic cmp_signed; + + always_comb begin + unique case (operator_i) + ALU_GE, + ALU_LT, + ALU_SLT, + // RV32B only + ALU_MIN, + ALU_MAX: cmp_signed = 1'b1; + + default: cmp_signed = 1'b0; + endcase + end + + assign is_equal = (adder_result == 32'b0); + assign is_equal_result_o = is_equal; + + // Is greater equal + always_comb begin + if ((operand_a_i[31] ^ operand_b_i[31]) == 1'b0) begin + is_greater_equal = (adder_result[31] == 1'b0); + end else begin + is_greater_equal = operand_a_i[31] ^ (cmp_signed); + end + end + + // GTE unsigned: + // (a[31] == 1 && b[31] == 1) => adder_result[31] == 0 + // (a[31] == 0 && b[31] == 0) => adder_result[31] == 0 + // (a[31] == 1 && b[31] == 0) => 1 + // (a[31] == 0 && b[31] == 1) => 0 + + // GTE signed: + // (a[31] == 1 && b[31] == 1) => adder_result[31] == 0 + // (a[31] == 0 && b[31] == 0) => adder_result[31] == 0 + // (a[31] == 1 && b[31] == 0) => 0 + // (a[31] == 0 && b[31] == 1) => 1 + + // generate comparison result + logic cmp_result; + + always_comb begin + unique case (operator_i) + ALU_EQ: cmp_result = is_equal; + ALU_NE: cmp_result = ~is_equal; + ALU_GE, ALU_GEU, + ALU_MAX, ALU_MAXU: cmp_result = is_greater_equal; // RV32B only + ALU_LT, ALU_LTU, + ALU_MIN, ALU_MINU, //RV32B only + ALU_SLT, ALU_SLTU: cmp_result = ~is_greater_equal; + + default: cmp_result = is_equal; + endcase + end + + assign comparison_result_o = cmp_result; + + /////////// + // Shift // + /////////// + + // The shifter structure consists of a 33-bit shifter: 32-bit operand + 1 bit extension for + // arithmetic shifts and one-shift support. + // Rotations and funnel shifts are implemented as multi-cycle instructions. + // The shifter is also used for single-bit instructions and bit-field place as detailed below. + // + // Standard Shifts + // =============== + // For standard shift instructions, the direction of the shift is to the right by default. For + // left shifts, the signal shift_left signal is set. If so, the operand is initially reversed, + // shifted to the right by the specified amount and shifted back again. For arithmetic- and + // one-shifts the 33rd bit of the shifter operand can is set accordingly. + // + // Multicycle Shifts + // ================= + // + // Rotation + // -------- + // For rotations, the operand signals operand_a_i and operand_b_i are kept constant to rs1 and + // rs2 respectively. + // + // Rotation pseudocode: + // shift_amt = rs2 & 31; + // multicycle_result = (rs1 >> shift_amt) | (rs1 << (32 - shift_amt)); + // ^-- cycle 0 -----^ ^-- cycle 1 --------------^ + // + // Funnel Shifts + // ------------- + // For funnel shifs, operand_a_i is tied to rs1 in the first cycle and rs3 in the + // second cycle. operand_b_i is always tied to rs2. The order of applying the shift amount or + // its complement is determined by bit [5] of shift_amt. + // + // Funnel shift Pseudocode: (fsl) + // shift_amt = rs2 & 63; + // shift_amt_compl = 32 - shift_amt[4:0] + // if (shift_amt >=33): + // multicycle_result = (rs1 >> shift_amt_compl[4:0]) | (rs3 << shift_amt[4:0]); + // ^-- cycle 0 ----------------^ ^-- cycle 1 ------------^ + // else if (shift_amt <= 31 && shift_amt > 0): + // multicycle_result = (rs1 << shift_amt[4:0]) | (rs3 >> shift_amt_compl[4:0]); + // ^-- cycle 0 ----------^ ^-- cycle 1 -------------------^ + // For shift_amt == 0, 32, both shift_amt[4:0] and shift_amt_compl[4:0] == '0. + // these cases need to be handled separately outside the shifting structure: + // else if (shift_amt == 32): + // multicycle_result = rs3 + // else if (shift_amt == 0): + // multicycle_result = rs1. + // + // Single-Bit Instructions + // ======================= + // Single bit instructions operate on bit operand_b_i[4:0] of operand_a_i. + + // The operations sbset, sbclr and sbinv are implemented by generation of a bit-mask using the + // shifter structure. This is done by left-shifting the operand 32'h1 by the required amount. + // The signal shift_sbmode multiplexes the shifter input and sets the signal shift_left. + // Further processing is taken care of by a separate structure. + // + // For sbext, the bit defined by operand_b_i[4:0] is to be returned. This is done by simply + // shifting operand_a_i to the right by the required amount and returning bit [0] of the result. + // + // Bit-Field Place + // =============== + // The shifter structure is shared to compute bfp_mask << bfp_off. + + logic shift_left; + logic shift_ones; + logic shift_arith; + logic shift_funnel; + logic shift_sbmode; + logic [5:0] shift_amt; + logic [5:0] shift_amt_compl; // complementary shift amount (32 - shift_amt) + + logic [31:0] shift_operand; + logic [32:0] shift_result_ext; + logic unused_shift_result_ext; + logic [31:0] shift_result; + logic [31:0] shift_result_rev; + + // zbf + logic bfp_op; + logic [4:0] bfp_len; + logic [4:0] bfp_off; + logic [31:0] bfp_mask; + logic [31:0] bfp_mask_rev; + logic [31:0] bfp_result; + + // bfp: shares the shifter structure to compute bfp_mask << bfp_off + assign bfp_op = (RV32B != RV32BNone) ? (operator_i == ALU_BFP) : 1'b0; + assign bfp_len = {~(|operand_b_i[27:24]), operand_b_i[27:24]}; // len = 0 encodes for len = 16 + assign bfp_off = operand_b_i[20:16]; + assign bfp_mask = (RV32B != RV32BNone) ? ~(32'hffff_ffff << bfp_len) : '0; + for (genvar i=0; i<32; i++) begin : gen_rev_bfp_mask + assign bfp_mask_rev[i] = bfp_mask[31-i]; + end + + assign bfp_result =(RV32B != RV32BNone) ? + (~shift_result & operand_a_i) | ((operand_b_i & bfp_mask) << bfp_off) : '0; + + // bit shift_amt[5]: word swap bit: only considered for FSL/FSR. + // if set, reverse operations in first and second cycle. + assign shift_amt[5] = operand_b_i[5] & shift_funnel; + assign shift_amt_compl = 32 - operand_b_i[4:0]; + + always_comb begin + if (bfp_op) begin + shift_amt[4:0] = bfp_off ; // length field of bfp control word + end else begin + shift_amt[4:0] = instr_first_cycle_i ? + (operand_b_i[5] && shift_funnel ? shift_amt_compl[4:0] : operand_b_i[4:0]) : + (operand_b_i[5] && shift_funnel ? operand_b_i[4:0] : shift_amt_compl[4:0]); + end + end + + // single-bit mode: shift + assign shift_sbmode = (RV32B != RV32BNone) ? + (operator_i == ALU_SBSET) | (operator_i == ALU_SBCLR) | (operator_i == ALU_SBINV) : 1'b0; + + // left shift if this is: + // * a standard left shift (slo, sll) + // * a rol in the first cycle + // * a ror in the second cycle + // * fsl: without word-swap bit: first cycle, else: second cycle + // * fsr: without word-swap bit: second cycle, else: first cycle + // * a single-bit instruction: sbclr, sbset, sbinv (excluding sbext) + // * bfp: bfp_mask << bfp_off + always_comb begin + unique case (operator_i) + ALU_SLL: shift_left = 1'b1; + ALU_SLO, + ALU_BFP: shift_left = (RV32B != RV32BNone) ? 1'b1 : 1'b0; + ALU_ROL: shift_left = (RV32B != RV32BNone) ? instr_first_cycle_i : 0; + ALU_ROR: shift_left = (RV32B != RV32BNone) ? ~instr_first_cycle_i : 0; + ALU_FSL: shift_left = (RV32B != RV32BNone) ? + (shift_amt[5] ? ~instr_first_cycle_i : instr_first_cycle_i) : 1'b0; + ALU_FSR: shift_left = (RV32B != RV32BNone) ? + (shift_amt[5] ? instr_first_cycle_i : ~instr_first_cycle_i) : 1'b0; + default: shift_left = 1'b0; + endcase + if (shift_sbmode) begin + shift_left = 1'b1; + end + end + + assign shift_arith = (operator_i == ALU_SRA); + assign shift_ones = + (RV32B != RV32BNone) ? (operator_i == ALU_SLO) | (operator_i == ALU_SRO) : 1'b0; + assign shift_funnel = + (RV32B != RV32BNone) ? (operator_i == ALU_FSL) | (operator_i == ALU_FSR) : 1'b0; + + // shifter structure. + always_comb begin + // select shifter input + // for bfp, sbmode and shift_left the corresponding bit-reversed input is chosen. + if (RV32B == RV32BNone) begin + shift_operand = shift_left ? operand_a_rev : operand_a_i; + end else begin + unique case (1'b1) + bfp_op: shift_operand = bfp_mask_rev; + shift_sbmode: shift_operand = 32'h8000_0000; + default: shift_operand = shift_left ? operand_a_rev : operand_a_i; + endcase + end + + shift_result_ext = + $unsigned($signed({shift_ones | (shift_arith & shift_operand[31]), shift_operand}) >>> + shift_amt[4:0]); + + shift_result = shift_result_ext[31:0]; + unused_shift_result_ext = shift_result_ext[32]; + + for (int unsigned i=0; i<32; i++) begin + shift_result_rev[i] = shift_result[31-i]; + end + + shift_result = shift_left ? shift_result_rev : shift_result; + + end + + /////////////////// + // Bitwise Logic // + /////////////////// + + logic bwlogic_or; + logic bwlogic_and; + logic [31:0] bwlogic_operand_b; + logic [31:0] bwlogic_or_result; + logic [31:0] bwlogic_and_result; + logic [31:0] bwlogic_xor_result; + logic [31:0] bwlogic_result; + + logic bwlogic_op_b_negate; + + always_comb begin + unique case (operator_i) + // Logic-with-negate OPs (RV32B Ops) + ALU_XNOR, + ALU_ORN, + ALU_ANDN: bwlogic_op_b_negate = (RV32B != RV32BNone) ? 1'b1 : 1'b0; + ALU_CMIX: bwlogic_op_b_negate = (RV32B != RV32BNone) ? ~instr_first_cycle_i : 1'b0; + default: bwlogic_op_b_negate = 1'b0; + endcase + end + + assign bwlogic_operand_b = bwlogic_op_b_negate ? operand_b_neg[32:1] : operand_b_i; + + assign bwlogic_or_result = operand_a_i | bwlogic_operand_b; + assign bwlogic_and_result = operand_a_i & bwlogic_operand_b; + assign bwlogic_xor_result = operand_a_i ^ bwlogic_operand_b; + + assign bwlogic_or = (operator_i == ALU_OR) | (operator_i == ALU_ORN); + assign bwlogic_and = (operator_i == ALU_AND) | (operator_i == ALU_ANDN); + + always_comb begin + unique case (1'b1) + bwlogic_or: bwlogic_result = bwlogic_or_result; + bwlogic_and: bwlogic_result = bwlogic_and_result; + default: bwlogic_result = bwlogic_xor_result; + endcase + end + + logic [5:0] bitcnt_result; + logic [31:0] minmax_result; + logic [31:0] pack_result; + logic [31:0] sext_result; + logic [31:0] singlebit_result; + logic [31:0] rev_result; + logic [31:0] shuffle_result; + logic [31:0] butterfly_result; + logic [31:0] invbutterfly_result; + logic [31:0] clmul_result; + logic [31:0] multicycle_result; + + if (RV32B != RV32BNone) begin : g_alu_rvb + + ///////////////// + // Bitcounting // + ///////////////// + + // The bit-counter structure computes the number of set bits in its operand. Partial results + // (from left to right) are needed to compute the control masks for computation of bext/bdep + // by the butterfly network, if implemented. + // For pcnt, clz and ctz, only the end result is used. + + logic zbe_op; + logic bitcnt_ctz; + logic bitcnt_clz; + logic bitcnt_cz; + logic [31:0] bitcnt_bits; + logic [31:0] bitcnt_mask_op; + logic [31:0] bitcnt_bit_mask; + logic [ 5:0] bitcnt_partial [32]; + logic [31:0] bitcnt_partial_lsb_d; + logic [31:0] bitcnt_partial_msb_d; + + + assign bitcnt_ctz = operator_i == ALU_CTZ; + assign bitcnt_clz = operator_i == ALU_CLZ; + assign bitcnt_cz = bitcnt_ctz | bitcnt_clz; + assign bitcnt_result = bitcnt_partial[31]; + + // Bit-mask generation for clz and ctz: + // The bit mask is generated by spreading the lowest-order set bit in the operand to all + // higher order bits. The resulting mask is inverted to cover the lowest order zeros. In order + // to create the bit mask for leading zeros, the input operand needs to be reversed. + assign bitcnt_mask_op = bitcnt_clz ? operand_a_rev : operand_a_i; + + always_comb begin + bitcnt_bit_mask = bitcnt_mask_op; + bitcnt_bit_mask |= bitcnt_bit_mask << 1; + bitcnt_bit_mask |= bitcnt_bit_mask << 2; + bitcnt_bit_mask |= bitcnt_bit_mask << 4; + bitcnt_bit_mask |= bitcnt_bit_mask << 8; + bitcnt_bit_mask |= bitcnt_bit_mask << 16; + bitcnt_bit_mask = ~bitcnt_bit_mask; + end + + assign zbe_op = (operator_i == ALU_BEXT) | (operator_i == ALU_BDEP); + + always_comb begin + case(1'b1) + zbe_op: bitcnt_bits = operand_b_i; + bitcnt_cz: bitcnt_bits = bitcnt_bit_mask & ~bitcnt_mask_op; // clz / ctz + default: bitcnt_bits = operand_a_i; // pcnt + endcase + end + + // The parallel prefix counter is of the structure of a Brent-Kung Adder. In the first + // log2(width) stages, the sum of the n preceding bit lines is computed for the bit lines at + // positions 2**n-1 (power-of-two positions) where n denotes the current stage. + // In stage n=log2(width), the count for position width-1 (the MSB) is finished. + // For the intermediate values, an inverse adder tree then computes the bit counts for the bit + // lines at positions + // m = 2**(n-1) + i*2**(n-2), where i = [1 ... width / 2**(n-1)-1] and n = [log2(width) ... 2]. + // Thus, at every subsequent stage the result of two previously unconnected sub-trees is + // summed, starting at the node summing bits [width/2-1 : 0] and [3*width/4-1: width/2] + // and moving to iteratively sum up all the sub-trees. + // The inverse adder tree thus features log2(width) - 1 stages the first of these stages is a + // single addition at position 3*width/4 - 1. It does not interfere with the last + // stage of the primary adder tree. These stages can thus be folded together, resulting in a + // total of 2*log2(width)-2 stages. + // For more details refer to R. Brent, H. T. Kung, "A Regular Layout for Parallel Adders", + // (1982). + // For a bitline at position p, only bits + // bitcnt_partial[max(i, such that p % log2(i) == 0)-1 : 0] are needed for generation of the + // butterfly network control signals. The adders in the intermediate value adder tree thus need + // not be full 5-bit adders. We leave the optimization to the synthesis tools. + // + // Consider the following 8-bit example for illustraton. + // + // let bitcnt_bits = 8'babcdefgh. + // + // a b c d e f g h + // | /: | /: | /: | /: + // |/ : |/ : |/ : |/ : + // stage 1: + : + : + : + : + // | : /: : | : /: : + // |,--+ : : |,--+ : : + // stage 2: + : : : + : : : + // | : | : /: : : : + // |,-----,--+ : : : : ^-primary adder tree + // stage 3: + : + : : : : : ------------------------- + // : | /| /| /| /| /| : ,-intermediate adder tree + // : |/ |/ |/ |/ |/ : : + // stage 4 : + + + + + : : + // : : : : : : : : + // bitcnt_partial[i] 7 6 5 4 3 2 1 0 + + always_comb begin + bitcnt_partial = '{default: '0}; + // stage 1 + for (int unsigned i=1; i<32; i+=2) begin + bitcnt_partial[i] = {5'h0, bitcnt_bits[i]} + {5'h0, bitcnt_bits[i-1]}; + end + // stage 2 + for (int unsigned i=3; i<32; i+=4) begin + bitcnt_partial[i] = bitcnt_partial[i-2] + bitcnt_partial[i]; + end + // stage 3 + for (int unsigned i=7; i<32; i+=8) begin + bitcnt_partial[i] = bitcnt_partial[i-4] + bitcnt_partial[i]; + end + // stage 4 + for (int unsigned i=15; i <32; i+=16) begin + bitcnt_partial[i] = bitcnt_partial[i-8] + bitcnt_partial[i]; + end + // stage 5 + bitcnt_partial[31] = bitcnt_partial[15] + bitcnt_partial[31]; + // ^- primary adder tree + // ------------------------------- + // ,-intermediate value adder tree + bitcnt_partial[23] = bitcnt_partial[15] + bitcnt_partial[23]; + + // stage 6 + for (int unsigned i=11; i<32; i+=8) begin + bitcnt_partial[i] = bitcnt_partial[i-4] + bitcnt_partial[i]; + end + + // stage 7 + for (int unsigned i=5; i<32; i+=4) begin + bitcnt_partial[i] = bitcnt_partial[i-2] + bitcnt_partial[i]; + end + // stage 8 + bitcnt_partial[0] = {5'h0, bitcnt_bits[0]}; + for (int unsigned i=2; i<32; i+=2) begin + bitcnt_partial[i] = bitcnt_partial[i-1] + {5'h0, bitcnt_bits[i]}; + end + end + + /////////////// + // Min / Max // + /////////////// + + assign minmax_result = cmp_result ? operand_a_i : operand_b_i; + + ////////// + // Pack // + ////////// + + logic packu; + logic packh; + assign packu = operator_i == ALU_PACKU; + assign packh = operator_i == ALU_PACKH; + + always_comb begin + unique case (1'b1) + packu: pack_result = {operand_b_i[31:16], operand_a_i[31:16]}; + packh: pack_result = {16'h0, operand_b_i[7:0], operand_a_i[7:0]}; + default: pack_result = {operand_b_i[15:0], operand_a_i[15:0]}; + endcase + end + + ////////// + // Sext // + ////////// + + assign sext_result = (operator_i == ALU_SEXTB) ? + { {24{operand_a_i[7]}}, operand_a_i[7:0]} : { {16{operand_a_i[15]}}, operand_a_i[15:0]}; + + ///////////////////////////// + // Single-bit Instructions // + ///////////////////////////// + + always_comb begin + unique case (operator_i) + ALU_SBSET: singlebit_result = operand_a_i | shift_result; + ALU_SBCLR: singlebit_result = operand_a_i & ~shift_result; + ALU_SBINV: singlebit_result = operand_a_i ^ shift_result; + default: singlebit_result = {31'h0, shift_result[0]}; // ALU_SBEXT + endcase + end + + //////////////////////////////////// + // General Reverse and Or-combine // + //////////////////////////////////// + + // Only a subset of the General reverse and or-combine instructions are implemented in the + // balanced version of the B extension. Currently rev, rev8 and orc.b are supported in the + // base extension. + + logic [4:0] zbp_shift_amt; + logic gorc_op; + + assign gorc_op = (operator_i == ALU_GORC); + assign zbp_shift_amt[2:0] = (RV32B == RV32BFull) ? shift_amt[2:0] : {3{&shift_amt[2:0]}}; + assign zbp_shift_amt[4:3] = (RV32B == RV32BFull) ? shift_amt[4:3] : {2{&shift_amt[4:3]}}; + + always_comb begin + rev_result = operand_a_i; + + if (zbp_shift_amt[0]) begin + rev_result = (gorc_op ? rev_result : 32'h0) | + ((rev_result & 32'h5555_5555) << 1) | + ((rev_result & 32'haaaa_aaaa) >> 1); + end + + if (zbp_shift_amt[1]) begin + rev_result = (gorc_op ? rev_result : 32'h0) | + ((rev_result & 32'h3333_3333) << 2) | + ((rev_result & 32'hcccc_cccc) >> 2); + end + + if (zbp_shift_amt[2]) begin + rev_result = (gorc_op ? rev_result : 32'h0) | + ((rev_result & 32'h0f0f_0f0f) << 4) | + ((rev_result & 32'hf0f0_f0f0) >> 4); + end + + if (zbp_shift_amt[3]) begin + rev_result = (gorc_op & (RV32B == RV32BFull) ? rev_result : 32'h0) | + ((rev_result & 32'h00ff_00ff) << 8) | + ((rev_result & 32'hff00_ff00) >> 8); + end + + if (zbp_shift_amt[4]) begin + rev_result = (gorc_op & (RV32B == RV32BFull) ? rev_result : 32'h0) | + ((rev_result & 32'h0000_ffff) << 16) | + ((rev_result & 32'hffff_0000) >> 16); + end + end + + logic crc_hmode; + logic crc_bmode; + logic [31:0] clmul_result_rev; + + if (RV32B == RV32BFull) begin : gen_alu_rvb_full + + ///////////////////////// + // Shuffle / Unshuffle // + ///////////////////////// + + localparam logic [31:0] SHUFFLE_MASK_L [4] = + '{32'h00ff_0000, 32'h0f00_0f00, 32'h3030_3030, 32'h4444_4444}; + localparam logic [31:0] SHUFFLE_MASK_R [4] = + '{32'h0000_ff00, 32'h00f0_00f0, 32'h0c0c_0c0c, 32'h2222_2222}; + + localparam logic [31:0] FLIP_MASK_L [4] = + '{32'h2200_1100, 32'h0044_0000, 32'h4411_0000, 32'h1100_0000}; + localparam logic [31:0] FLIP_MASK_R [4] = + '{32'h0088_0044, 32'h0000_2200, 32'h0000_8822, 32'h0000_0088}; + + logic [31:0] SHUFFLE_MASK_NOT [4]; + for(genvar i = 0; i < 4; i++) begin : gen_shuffle_mask_not + assign SHUFFLE_MASK_NOT[i] = ~(SHUFFLE_MASK_L[i] | SHUFFLE_MASK_R[i]); + end + + logic shuffle_flip; + assign shuffle_flip = operator_i == ALU_UNSHFL; + + logic [3:0] shuffle_mode; + + always_comb begin + shuffle_result = operand_a_i; + + if (shuffle_flip) begin + shuffle_mode[3] = shift_amt[0]; + shuffle_mode[2] = shift_amt[1]; + shuffle_mode[1] = shift_amt[2]; + shuffle_mode[0] = shift_amt[3]; + end else begin + shuffle_mode = shift_amt[3:0]; + end + + if (shuffle_flip) begin + shuffle_result = (shuffle_result & 32'h8822_4411) | + ((shuffle_result << 6) & FLIP_MASK_L[0]) | + ((shuffle_result >> 6) & FLIP_MASK_R[0]) | + ((shuffle_result << 9) & FLIP_MASK_L[1]) | + ((shuffle_result >> 9) & FLIP_MASK_R[1]) | + ((shuffle_result << 15) & FLIP_MASK_L[2]) | + ((shuffle_result >> 15) & FLIP_MASK_R[2]) | + ((shuffle_result << 21) & FLIP_MASK_L[3]) | + ((shuffle_result >> 21) & FLIP_MASK_R[3]); + end + + if (shuffle_mode[3]) begin + shuffle_result = (shuffle_result & SHUFFLE_MASK_NOT[0]) | + (((shuffle_result << 8) & SHUFFLE_MASK_L[0]) | + ((shuffle_result >> 8) & SHUFFLE_MASK_R[0])); + end + if (shuffle_mode[2]) begin + shuffle_result = (shuffle_result & SHUFFLE_MASK_NOT[1]) | + (((shuffle_result << 4) & SHUFFLE_MASK_L[1]) | + ((shuffle_result >> 4) & SHUFFLE_MASK_R[1])); + end + if (shuffle_mode[1]) begin + shuffle_result = (shuffle_result & SHUFFLE_MASK_NOT[2]) | + (((shuffle_result << 2) & SHUFFLE_MASK_L[2]) | + ((shuffle_result >> 2) & SHUFFLE_MASK_R[2])); + end + if (shuffle_mode[0]) begin + shuffle_result = (shuffle_result & SHUFFLE_MASK_NOT[3]) | + (((shuffle_result << 1) & SHUFFLE_MASK_L[3]) | + ((shuffle_result >> 1) & SHUFFLE_MASK_R[3])); + end + + if (shuffle_flip) begin + shuffle_result = (shuffle_result & 32'h8822_4411) | + ((shuffle_result << 6) & FLIP_MASK_L[0]) | + ((shuffle_result >> 6) & FLIP_MASK_R[0]) | + ((shuffle_result << 9) & FLIP_MASK_L[1]) | + ((shuffle_result >> 9) & FLIP_MASK_R[1]) | + ((shuffle_result << 15) & FLIP_MASK_L[2]) | + ((shuffle_result >> 15) & FLIP_MASK_R[2]) | + ((shuffle_result << 21) & FLIP_MASK_L[3]) | + ((shuffle_result >> 21) & FLIP_MASK_R[3]); + end + end + + /////////////// + // Butterfly // + /////////////// + + // The butterfly / inverse butterfly network executing bext/bdep (zbe) instructions. + // For bdep, the control bits mask of a local left region is generated by + // the inverse of a n-bit left rotate and complement upon wrap (LROTC) operation by the number + // of ones in the deposit bitmask to the right of the segment. n hereby denotes the width + // of the according segment. The bitmask for a pertaining local right region is equal to the + // corresponding local left region. Bext uses an analogue inverse process. + // Consider the following 8-bit example. For details, see Hilewitz et al. "Fast Bit Gather, + // Bit Scatter and Bit Permuation Instructions for Commodity Microprocessors", (2008). + // + // The bext/bdep instructions are completed in 2 cycles. In the first cycle, the control + // bitmask is prepared by executing the parallel prefix bit count. In the second cycle, + // the bit swapping is executed according to the control masks. + + // 8-bit example: (Hilewitz et al.) + // Consider the instruction bdep operand_a_i deposit_mask + // Let operand_a_i = 8'babcd_efgh + // deposit_mask = 8'b1010_1101 + // + // control bitmask for stage 1: + // - number of ones in the right half of the deposit bitmask: 3 + // - width of the segment: 4 + // - control bitmask = ~LROTC(4'b0, 3)[3:0] = 4'b1000 + // + // control bitmask: c3 c2 c1 c0 c3 c2 c1 c0 + // 1 0 0 0 1 0 0 0 + // <- L -----> <- R -----> + // operand_a_i a b c d e f g h + // :\ | | | /: | | | + // : +|---|--|-+ : | | | + // :/ | | | \: | | | + // stage 1 e b c d a f g h + // <L-> <R-> <L-> <R-> + // control bitmask: c3 c2 c3 c2 c1 c0 c1 c0 + // 1 1 1 1 1 0 1 0 + // :\ :\ /: /: :\ | /: | + // : +:-+-:+ : : +|-+ : | + // :/ :/ \: \: :/ | \: | + // stage 2 c d e b g f a h + // L R L R L R L R + // control bitmask: c3 c3 c2 c2 c1 c1 c0 c0 + // 1 1 0 0 1 1 0 0 + // :\/: | | :\/: | | + // : : | | : : | | + // :/\: | | :/\: | | + // stage 3 d c e b f g a h + // & deposit bitmask: 1 0 1 0 1 1 0 1 + // result: d 0 e 0 f g 0 h + + logic [ 5:0] bitcnt_partial_q [32]; + + // first cycle + // Store partial bitcnts + for (genvar i=0; i<32; i++) begin : gen_bitcnt_reg_in_lsb + assign bitcnt_partial_lsb_d[i] = bitcnt_partial[i][0]; + end + + for (genvar i=0; i<16; i++) begin : gen_bitcnt_reg_in_b1 + assign bitcnt_partial_msb_d[i] = bitcnt_partial[2*i+1][1]; + end + + for (genvar i=0; i<8; i++) begin : gen_bitcnt_reg_in_b2 + assign bitcnt_partial_msb_d[16+i] = bitcnt_partial[4*i+3][2]; + end + + for (genvar i=0; i<4; i++) begin : gen_bitcnt_reg_in_b3 + assign bitcnt_partial_msb_d[24+i] = bitcnt_partial[8*i+7][3]; + end + + for (genvar i=0; i<2; i++) begin : gen_bitcnt_reg_in_b4 + assign bitcnt_partial_msb_d[28+i] = bitcnt_partial[16*i+15][4]; + end + + assign bitcnt_partial_msb_d[30] = bitcnt_partial[31][5]; + assign bitcnt_partial_msb_d[31] = 1'b0; // unused + + // Second cycle + // Load partial bitcnts + always_comb begin + bitcnt_partial_q = '{default: '0}; + + for (int unsigned i=0; i<32; i++) begin : gen_bitcnt_reg_out_lsb + bitcnt_partial_q[i][0] = imd_val_q_i[0][i]; + end + + for (int unsigned i=0; i<16; i++) begin : gen_bitcnt_reg_out_b1 + bitcnt_partial_q[2*i+1][1] = imd_val_q_i[1][i]; + end + + for (int unsigned i=0; i<8; i++) begin : gen_bitcnt_reg_out_b2 + bitcnt_partial_q[4*i+3][2] = imd_val_q_i[1][16+i]; + end + + for (int unsigned i=0; i<4; i++) begin : gen_bitcnt_reg_out_b3 + bitcnt_partial_q[8*i+7][3] = imd_val_q_i[1][24+i]; + end + + for (int unsigned i=0; i<2; i++) begin : gen_bitcnt_reg_out_b4 + bitcnt_partial_q[16*i+15][4] = imd_val_q_i[1][28+i]; + end + + bitcnt_partial_q[31][5] = imd_val_q_i[1][30]; + end + + logic [31:0] butterfly_mask_l[5]; + logic [31:0] butterfly_mask_r[5]; + logic [31:0] butterfly_mask_not[5]; + logic [31:0] lrotc_stage [5]; // left rotate and complement upon wrap + + // number of bits in local r = 32 / 2**(stage + 1) = 16/2**stage + `define _N(stg) (16 >> stg) + + // bext / bdep control bit generation + for (genvar stg=0; stg<5; stg++) begin : gen_butterfly_ctrl_stage + // number of segs: 2** stg + for (genvar seg=0; seg<2**stg; seg++) begin : gen_butterfly_ctrl + + assign lrotc_stage[stg][2*`_N(stg)*(seg+1)-1 : 2*`_N(stg)*seg] = + {{`_N(stg){1'b0}},{`_N(stg){1'b1}}} << + bitcnt_partial_q[`_N(stg)*(2*seg+1)-1][$clog2(`_N(stg)):0]; + + assign butterfly_mask_l[stg][`_N(stg)*(2*seg+2)-1 : `_N(stg)*(2*seg+1)] + = ~lrotc_stage[stg][`_N(stg)*(2*seg+2)-1 : `_N(stg)*(2*seg+1)]; + + assign butterfly_mask_r[stg][`_N(stg)*(2*seg+1)-1 : `_N(stg)*(2*seg)] + = ~lrotc_stage[stg][`_N(stg)*(2*seg+2)-1 : `_N(stg)*(2*seg+1)]; + + assign butterfly_mask_l[stg][`_N(stg)*(2*seg+1)-1 : `_N(stg)*(2*seg)] = '0; + assign butterfly_mask_r[stg][`_N(stg)*(2*seg+2)-1 : `_N(stg)*(2*seg+1)] = '0; + end + end + `undef _N + + for (genvar stg=0; stg<5; stg++) begin : gen_butterfly_not + assign butterfly_mask_not[stg] = + ~(butterfly_mask_l[stg] | butterfly_mask_r[stg]); + end + + always_comb begin + butterfly_result = operand_a_i; + + butterfly_result = butterfly_result & butterfly_mask_not[0] | + ((butterfly_result & butterfly_mask_l[0]) >> 16)| + ((butterfly_result & butterfly_mask_r[0]) << 16); + + butterfly_result = butterfly_result & butterfly_mask_not[1] | + ((butterfly_result & butterfly_mask_l[1]) >> 8)| + ((butterfly_result & butterfly_mask_r[1]) << 8); + + butterfly_result = butterfly_result & butterfly_mask_not[2] | + ((butterfly_result & butterfly_mask_l[2]) >> 4)| + ((butterfly_result & butterfly_mask_r[2]) << 4); + + butterfly_result = butterfly_result & butterfly_mask_not[3] | + ((butterfly_result & butterfly_mask_l[3]) >> 2)| + ((butterfly_result & butterfly_mask_r[3]) << 2); + + butterfly_result = butterfly_result & butterfly_mask_not[4] | + ((butterfly_result & butterfly_mask_l[4]) >> 1)| + ((butterfly_result & butterfly_mask_r[4]) << 1); + + butterfly_result = butterfly_result & operand_b_i; + end + + always_comb begin + invbutterfly_result = operand_a_i & operand_b_i; + + invbutterfly_result = invbutterfly_result & butterfly_mask_not[4] | + ((invbutterfly_result & butterfly_mask_l[4]) >> 1)| + ((invbutterfly_result & butterfly_mask_r[4]) << 1); + + invbutterfly_result = invbutterfly_result & butterfly_mask_not[3] | + ((invbutterfly_result & butterfly_mask_l[3]) >> 2)| + ((invbutterfly_result & butterfly_mask_r[3]) << 2); + + invbutterfly_result = invbutterfly_result & butterfly_mask_not[2] | + ((invbutterfly_result & butterfly_mask_l[2]) >> 4)| + ((invbutterfly_result & butterfly_mask_r[2]) << 4); + + invbutterfly_result = invbutterfly_result & butterfly_mask_not[1] | + ((invbutterfly_result & butterfly_mask_l[1]) >> 8)| + ((invbutterfly_result & butterfly_mask_r[1]) << 8); + + invbutterfly_result = invbutterfly_result & butterfly_mask_not[0] | + ((invbutterfly_result & butterfly_mask_l[0]) >> 16)| + ((invbutterfly_result & butterfly_mask_r[0]) << 16); + end + + /////////////////////////////////////////////////// + // Carry-less Multiply + Cyclic Redundancy Check // + /////////////////////////////////////////////////// + + // Carry-less multiplication can be understood as multiplication based on + // the addition interpreted as the bit-wise xor operation. + // + // Example: 1101 X 1011 = 1111111: + // + // 1011 X 1101 + // ----------- + // 1101 + // xor 1101 + // --------- + // 10111 + // xor 0000 + // ---------- + // 010111 + // xor 1101 + // ----------- + // 1111111 + // + // Architectural details: + // A 32 x 32-bit array + // [ operand_b[i] ? (operand_a << i) : '0 for i in 0 ... 31 ] + // is generated. The entries of the array are pairwise 'xor-ed' + // together in a 5-stage binary tree. + // + // + // Cyclic Redundancy Check: + // + // CRC-32 (CRC-32/ISO-HDLC) and CRC-32C (CRC-32/ISCSI) are directly implemented. For + // documentation of the crc configuration (crc-polynomials, initialization, reflection, etc.) + // see http://reveng.sourceforge.net/crc-catalogue/all.htm + // A useful guide to crc arithmetic and algorithms is given here: + // http://www.piclist.com/techref/method/math/crcguide.html. + // + // The CRC operation solves the following equation using binary polynomial arithmetic: + // + // rev(rd)(x) = rev(rs1)(x) * x**n mod {1, P}(x) + // + // where P denotes lower 32 bits of the corresponding CRC polynomial, rev(a) the bit reversal + // of a, n = 8,16, or 32 for .b, .h, .w -variants. {a, b} denotes bit concatenation. + // + // Using barret reduction, one can show that + // + // M(x) mod P(x) = R(x) = + // (M(x) * x**n) & {deg(P(x)'{1'b1}}) ^ (M(x) x**-(deg(P(x) - n)) cx mu(x) cx P(x), + // + // Where mu(x) = polydiv(x**64, {1,P}) & 0xffffffff. Here, 'cx' refers to carry-less + // multiplication. Substituting rev(rd)(x) for R(x) and rev(rs1)(x) for M(x) and solving for + // rd(x) with P(x) a crc32 polynomial (deg(P(x)) = 32), we get + // + // rd = rev( (rev(rs1) << n) ^ ((rev(rs1) >> (32-n)) cx mu cx P) + // = (rs1 >> n) ^ rev(rev( (rs1 << (32-n)) cx rev(mu)) cx P) + // ^-- cycle 0--------------------^ + // ^- cycle 1 -------------------------------------------^ + // + // In the last step we used the fact that carry-less multiplication is bit-order agnostic: + // rev(a cx b) = rev(a) cx rev(b). + + logic clmul_rmode; + logic clmul_hmode; + logic [31:0] clmul_op_a; + logic [31:0] clmul_op_b; + logic [31:0] operand_b_rev; + logic [31:0] clmul_and_stage[32]; + logic [31:0] clmul_xor_stage1[16]; + logic [31:0] clmul_xor_stage2[8]; + logic [31:0] clmul_xor_stage3[4]; + logic [31:0] clmul_xor_stage4[2]; + + logic [31:0] clmul_result_raw; + + for (genvar i=0; i<32; i++) begin: gen_rev_operand_b + assign operand_b_rev[i] = operand_b_i[31-i]; + end + + assign clmul_rmode = operator_i == ALU_CLMULR; + assign clmul_hmode = operator_i == ALU_CLMULH; + + // CRC + localparam logic [31:0] CRC32_POLYNOMIAL = 32'h04c1_1db7; + localparam logic [31:0] CRC32_MU_REV = 32'hf701_1641; + + localparam logic [31:0] CRC32C_POLYNOMIAL = 32'h1edc_6f41; + localparam logic [31:0] CRC32C_MU_REV = 32'hdea7_13f1; + + logic crc_op; + + logic crc_cpoly; + + logic [31:0] crc_operand; + logic [31:0] crc_poly; + logic [31:0] crc_mu_rev; + + assign crc_op = (operator_i == ALU_CRC32C_W) | (operator_i == ALU_CRC32_W) | + (operator_i == ALU_CRC32C_H) | (operator_i == ALU_CRC32_H) | + (operator_i == ALU_CRC32C_B) | (operator_i == ALU_CRC32_B); + + assign crc_cpoly = (operator_i == ALU_CRC32C_W) | + (operator_i == ALU_CRC32C_H) | + (operator_i == ALU_CRC32C_B); + + assign crc_hmode = (operator_i == ALU_CRC32_H) | (operator_i == ALU_CRC32C_H); + assign crc_bmode = (operator_i == ALU_CRC32_B) | (operator_i == ALU_CRC32C_B); + + assign crc_poly = crc_cpoly ? CRC32C_POLYNOMIAL : CRC32_POLYNOMIAL; + assign crc_mu_rev = crc_cpoly ? CRC32C_MU_REV : CRC32_MU_REV; + + always_comb begin + unique case(1'b1) + crc_bmode: crc_operand = {operand_a_i[7:0], 24'h0}; + crc_hmode: crc_operand = {operand_a_i[15:0], 16'h0}; + default: crc_operand = operand_a_i; + endcase + end + + // Select clmul input + always_comb begin + if (crc_op) begin + clmul_op_a = instr_first_cycle_i ? crc_operand : imd_val_q_i[0]; + clmul_op_b = instr_first_cycle_i ? crc_mu_rev : crc_poly; + end else begin + clmul_op_a = clmul_rmode | clmul_hmode ? operand_a_rev : operand_a_i; + clmul_op_b = clmul_rmode | clmul_hmode ? operand_b_rev : operand_b_i; + end + end + + for (genvar i=0; i<32; i++) begin : gen_clmul_and_op + assign clmul_and_stage[i] = clmul_op_b[i] ? clmul_op_a << i : '0; + end + + for (genvar i=0; i<16; i++) begin : gen_clmul_xor_op_l1 + assign clmul_xor_stage1[i] = clmul_and_stage[2*i] ^ clmul_and_stage[2*i+1]; + end + + for (genvar i=0; i<8; i++) begin : gen_clmul_xor_op_l2 + assign clmul_xor_stage2[i] = clmul_xor_stage1[2*i] ^ clmul_xor_stage1[2*i+1]; + end + + for (genvar i=0; i<4; i++) begin : gen_clmul_xor_op_l3 + assign clmul_xor_stage3[i] = clmul_xor_stage2[2*i] ^ clmul_xor_stage2[2*i+1]; + end + + for (genvar i=0; i<2; i++) begin : gen_clmul_xor_op_l4 + assign clmul_xor_stage4[i] = clmul_xor_stage3[2*i] ^ clmul_xor_stage3[2*i+1]; + end + + assign clmul_result_raw = clmul_xor_stage4[0] ^ clmul_xor_stage4[1]; + + for (genvar i=0; i<32; i++) begin : gen_rev_clmul_result + assign clmul_result_rev[i] = clmul_result_raw[31-i]; + end + + // clmulr_result = rev(clmul(rev(a), rev(b))) + // clmulh_result = clmulr_result >> 1 + always_comb begin + case(1'b1) + clmul_rmode: clmul_result = clmul_result_rev; + clmul_hmode: clmul_result = {1'b0, clmul_result_rev[31:1]}; + default: clmul_result = clmul_result_raw; + endcase + end + end else begin : gen_alu_rvb_notfull + logic [31:0] unused_imd_val_q_1; + assign unused_imd_val_q_1 = imd_val_q_i[1]; + assign shuffle_result = '0; + assign butterfly_result = '0; + assign invbutterfly_result = '0; + assign clmul_result = '0; + // support signals + assign bitcnt_partial_lsb_d = '0; + assign bitcnt_partial_msb_d = '0; + assign clmul_result_rev = '0; + assign crc_bmode = '0; + assign crc_hmode = '0; + end + + ////////////////////////////////////// + // Multicycle Bitmanip Instructions // + ////////////////////////////////////// + // Ternary instructions + Shift Rotations + Bit extract/deposit + CRC + // For ternary instructions (zbt), operand_a_i is tied to rs1 in the first cycle and rs3 in the + // second cycle. operand_b_i is always tied to rs2. + + always_comb begin + unique case (operator_i) + ALU_CMOV: begin + multicycle_result = (operand_b_i == 32'h0) ? operand_a_i : imd_val_q_i[0]; + imd_val_d_o = '{operand_a_i, 32'h0}; + if (instr_first_cycle_i) begin + imd_val_we_o = 2'b01; + end else begin + imd_val_we_o = 2'b00; + end + end + + ALU_CMIX: begin + multicycle_result = imd_val_q_i[0] | bwlogic_and_result; + imd_val_d_o = '{bwlogic_and_result, 32'h0}; + if (instr_first_cycle_i) begin + imd_val_we_o = 2'b01; + end else begin + imd_val_we_o = 2'b00; + end + end + + ALU_FSR, ALU_FSL, + ALU_ROL, ALU_ROR: begin + if (shift_amt[4:0] == 5'h0) begin + multicycle_result = shift_amt[5] ? operand_a_i : imd_val_q_i[0]; + end else begin + multicycle_result = imd_val_q_i[0] | shift_result; + end + imd_val_d_o = '{shift_result, 32'h0}; + if (instr_first_cycle_i) begin + imd_val_we_o = 2'b01; + end else begin + imd_val_we_o = 2'b00; + end + end + + ALU_CRC32_W, ALU_CRC32C_W, + ALU_CRC32_H, ALU_CRC32C_H, + ALU_CRC32_B, ALU_CRC32C_B: begin + if (RV32B == RV32BFull) begin + unique case(1'b1) + crc_bmode: multicycle_result = clmul_result_rev ^ (operand_a_i >> 8); + crc_hmode: multicycle_result = clmul_result_rev ^ (operand_a_i >> 16); + default: multicycle_result = clmul_result_rev; + endcase + imd_val_d_o = '{clmul_result_rev, 32'h0}; + if (instr_first_cycle_i) begin + imd_val_we_o = 2'b01; + end else begin + imd_val_we_o = 2'b00; + end + end else begin + imd_val_d_o = '{operand_a_i, 32'h0}; + imd_val_we_o = 2'b00; + multicycle_result = '0; + end + end + + ALU_BEXT, ALU_BDEP: begin + if (RV32B == RV32BFull) begin + multicycle_result = (operator_i == ALU_BDEP) ? butterfly_result : invbutterfly_result; + imd_val_d_o = '{bitcnt_partial_lsb_d, bitcnt_partial_msb_d}; + if (instr_first_cycle_i) begin + imd_val_we_o = 2'b11; + end else begin + imd_val_we_o = 2'b00; + end + end else begin + imd_val_d_o = '{operand_a_i, 32'h0}; + imd_val_we_o = 2'b00; + multicycle_result = '0; + end + end + + default: begin + imd_val_d_o = '{operand_a_i, 32'h0}; + imd_val_we_o = 2'b00; + multicycle_result = '0; + end + endcase + end + + + end else begin : g_no_alu_rvb + logic [31:0] unused_imd_val_q[2]; + assign unused_imd_val_q = imd_val_q_i; + logic [31:0] unused_butterfly_result; + assign unused_butterfly_result = butterfly_result; + logic [31:0] unused_invbutterfly_result; + assign unused_invbutterfly_result = invbutterfly_result; + // RV32B result signals + assign bitcnt_result = '0; + assign minmax_result = '0; + assign pack_result = '0; + assign sext_result = '0; + assign singlebit_result = '0; + assign rev_result = '0; + assign shuffle_result = '0; + assign butterfly_result = '0; + assign invbutterfly_result = '0; + assign clmul_result = '0; + assign multicycle_result = '0; + // RV32B support signals + assign imd_val_d_o = '{default: '0}; + assign imd_val_we_o = '{default: '0}; + end + + //////////////// + // Result mux // + //////////////// + + always_comb begin + result_o = '0; + + unique case (operator_i) + // Bitwise Logic Operations (negate: RV32B) + ALU_XOR, ALU_XNOR, + ALU_OR, ALU_ORN, + ALU_AND, ALU_ANDN: result_o = bwlogic_result; + + // Adder Operations + ALU_ADD, ALU_SUB: result_o = adder_result; + + // Shift Operations + ALU_SLL, ALU_SRL, + ALU_SRA, + // RV32B + ALU_SLO, ALU_SRO: result_o = shift_result; + + // Shuffle Operations (RV32B) + ALU_SHFL, ALU_UNSHFL: result_o = shuffle_result; + + // Comparison Operations + ALU_EQ, ALU_NE, + ALU_GE, ALU_GEU, + ALU_LT, ALU_LTU, + ALU_SLT, ALU_SLTU: result_o = {31'h0,cmp_result}; + + // MinMax Operations (RV32B) + ALU_MIN, ALU_MAX, + ALU_MINU, ALU_MAXU: result_o = minmax_result; + + // Bitcount Operations (RV32B) + ALU_CLZ, ALU_CTZ, + ALU_PCNT: result_o = {26'h0, bitcnt_result}; + + // Pack Operations (RV32B) + ALU_PACK, ALU_PACKH, + ALU_PACKU: result_o = pack_result; + + // Sign-Extend (RV32B) + ALU_SEXTB, ALU_SEXTH: result_o = sext_result; + + // Ternary Bitmanip Operations (RV32B) + ALU_CMIX, ALU_CMOV, + ALU_FSL, ALU_FSR, + // Rotate Shift (RV32B) + ALU_ROL, ALU_ROR, + // Cyclic Redundancy Checks (RV32B) + ALU_CRC32_W, ALU_CRC32C_W, + ALU_CRC32_H, ALU_CRC32C_H, + ALU_CRC32_B, ALU_CRC32C_B, + // Bit Extract / Deposit (RV32B) + ALU_BEXT, ALU_BDEP: result_o = multicycle_result; + + // Single-Bit Bitmanip Operations (RV32B) + ALU_SBSET, ALU_SBCLR, + ALU_SBINV, ALU_SBEXT: result_o = singlebit_result; + + // General Reverse / Or-combine (RV32B) + ALU_GREV, ALU_GORC: result_o = rev_result; + + // Bit Field Place (RV32B) + ALU_BFP: result_o = bfp_result; + + // Carry-less Multiply Operations (RV32B) + ALU_CLMUL, ALU_CLMULR, + ALU_CLMULH: result_o = clmul_result; + + default: ; + endcase + end + + logic unused_shift_amt_compl; + assign unused_shift_amt_compl = shift_amt_compl[5]; + +endmodule
diff --git a/verilog/rtl/brq_exu_multdiv_fast.sv b/verilog/rtl/brq_exu_multdiv_fast.sv new file mode 100644 index 0000000..3bb8217 --- /dev/null +++ b/verilog/rtl/brq_exu_multdiv_fast.sv
@@ -0,0 +1,514 @@ + +`define OP_L 15:0 +`define OP_H 31:16 + +/** + * Fast Multiplier and Division + * + * 16x16 kernel multiplier and Long Division + */ + + + +module brq_exu_multdiv_fast #( + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast + ) ( + input logic clk_i, + input logic rst_ni, + input logic mult_en_i, // dynamic enable signal, for FSM control + input logic div_en_i, // dynamic enable signal, for FSM control + input logic mult_sel_i, // static decoder output, for data muxes + input logic div_sel_i, // static decoder output, for data muxes + input brq_pkg::md_op_e operator_i, + input logic [1:0] signed_mode_i, + input logic [31:0] op_a_i, + input logic [31:0] op_b_i, + input logic [33:0] alu_adder_ext_i, + input logic [31:0] alu_adder_i, + input logic equal_to_zero_i, + input logic data_ind_timing_i, + + output logic [32:0] alu_operand_a_o, + output logic [32:0] alu_operand_b_o, + + input logic [33:0] imd_val_q_i[2], + output logic [33:0] imd_val_d_o[2], + output logic [1:0] imd_val_we_o, + + input logic multdiv_ready_id_i, + + output logic [31:0] multdiv_result_o, + output logic valid_o +); + + import brq_pkg::*; + + // Both multiplier variants + logic signed [34:0] mac_res_signed; + logic [34:0] mac_res_ext; + logic [33:0] accum; + logic sign_a, sign_b; + logic mult_valid; + logic signed_mult; + + // Results that become intermediate value depending on whether mul or div is being calculated + logic [33:0] mac_res_d, op_remainder_d; + // Raw output of MAC calculation + logic [33:0] mac_res; + + // Divider signals + logic div_sign_a, div_sign_b; + logic is_greater_equal; + logic div_change_sign, rem_change_sign; + logic [31:0] one_shift; + logic [31:0] op_denominator_q; + logic [31:0] op_numerator_q; + logic [31:0] op_quotient_q; + logic [31:0] op_denominator_d; + logic [31:0] op_numerator_d; + logic [31:0] op_quotient_d; + logic [31:0] next_remainder; + logic [32:0] next_quotient; + logic [31:0] res_adder_h; + logic div_valid; + logic [ 4:0] div_counter_q, div_counter_d; + logic multdiv_en; + logic mult_hold; + logic div_hold; + logic div_by_zero_d, div_by_zero_q; + + logic mult_en_internal; + logic div_en_internal; + + typedef enum logic [2:0] { + MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH + } md_fsm_e; + md_fsm_e md_state_q, md_state_d; + + logic unused_mult_sel_i; + assign unused_mult_sel_i = mult_sel_i; + + assign mult_en_internal = mult_en_i & ~mult_hold; + assign div_en_internal = div_en_i & ~div_hold; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + div_counter_q <= '0; + md_state_q <= MD_IDLE; + op_numerator_q <= '0; + op_quotient_q <= '0; + div_by_zero_q <= '0; + end else if (div_en_internal) begin + div_counter_q <= div_counter_d; + op_numerator_q <= op_numerator_d; + op_quotient_q <= op_quotient_d; + md_state_q <= md_state_d; + div_by_zero_q <= div_by_zero_d; + end + end + + + assign multdiv_en = mult_en_internal | div_en_internal; + + // Intermediate value register shared with ALU + assign imd_val_d_o[0] = div_sel_i ? op_remainder_d : mac_res_d; + assign imd_val_we_o[0] = multdiv_en; + + assign imd_val_d_o[1] = {2'b0, op_denominator_d}; + assign imd_val_we_o[1] = div_en_internal; + assign op_denominator_q = imd_val_q_i[1][31:0]; + logic [1:0] unused_imd_val; + assign unused_imd_val = imd_val_q_i[1][33:32]; + logic unused_mac_res_ext; + assign unused_mac_res_ext = mac_res_ext[34]; + + assign signed_mult = (signed_mode_i != 2'b00); + assign multdiv_result_o = div_sel_i ? imd_val_q_i[0][31:0] : mac_res_d[31:0]; + + // The single cycle multiplier uses three 17 bit multipliers to compute MUL instructions in a + // single cycle and MULH instructions in two cycles. + if (RV32M == RV32MSingleCycle) begin : gen_mult_single_cycle + + typedef enum logic { + MULL, MULH + } mult_fsm_e; + mult_fsm_e mult_state_q, mult_state_d; + + logic signed [33:0] mult1_res, mult2_res, mult3_res; + logic [33:0] mult1_res_uns; + logic [33:32] unused_mult1_res_uns; + logic [15:0] mult1_op_a, mult1_op_b; + logic [15:0] mult2_op_a, mult2_op_b; + logic [15:0] mult3_op_a, mult3_op_b; + logic mult1_sign_a, mult1_sign_b; + logic mult2_sign_a, mult2_sign_b; + logic mult3_sign_a, mult3_sign_b; + logic [33:0] summand1, summand2, summand3; + + assign mult1_res = $signed({mult1_sign_a, mult1_op_a}) * $signed({mult1_sign_b, mult1_op_b}); + assign mult2_res = $signed({mult2_sign_a, mult2_op_a}) * $signed({mult2_sign_b, mult2_op_b}); + assign mult3_res = $signed({mult3_sign_a, mult3_op_a}) * $signed({mult3_sign_b, mult3_op_b}); + + assign mac_res_signed = $signed(summand1) + $signed(summand2) + $signed(summand3); + + assign mult1_res_uns = $unsigned(mult1_res); + assign mac_res_ext = $unsigned(mac_res_signed); + assign mac_res = mac_res_ext[33:0]; + + assign sign_a = signed_mode_i[0] & op_a_i[31]; + assign sign_b = signed_mode_i[1] & op_b_i[31]; + + // The first two multipliers are only used in state 1 (MULL). We can assign them statically. + // al*bl + assign mult1_sign_a = 1'b0; + assign mult1_sign_b = 1'b0; + assign mult1_op_a = op_a_i[`OP_L]; + assign mult1_op_b = op_b_i[`OP_L]; + + // al*bh + assign mult2_sign_a = 1'b0; + assign mult2_sign_b = sign_b; + assign mult2_op_a = op_a_i[`OP_L]; + assign mult2_op_b = op_b_i[`OP_H]; + + // used in MULH + assign accum[17:0] = imd_val_q_i[0][33:16]; + assign accum[33:18] = {16{signed_mult & imd_val_q_i[0][33]}}; + + always_comb begin + // Default values == MULL + + // ah*bl + mult3_sign_a = sign_a; + mult3_sign_b = 1'b0; + mult3_op_a = op_a_i[`OP_H]; + mult3_op_b = op_b_i[`OP_L]; + + summand1 = {18'h0, mult1_res_uns[`OP_H]}; + summand2 = $unsigned(mult2_res); + summand3 = $unsigned(mult3_res); + + // mac_res = A*B[47:16], mult1_res = A*B[15:0] + mac_res_d = {2'b0, mac_res[`OP_L], mult1_res_uns[`OP_L]}; + mult_valid = mult_en_i; + mult_state_d = MULL; + + mult_hold = 1'b0; + + unique case (mult_state_q) + + MULL: begin + if (operator_i != MD_OP_MULL) begin + mac_res_d = mac_res; + mult_valid = 1'b0; + mult_state_d = MULH; + end else begin + mult_hold = ~multdiv_ready_id_i; + end + end + + MULH: begin + // ah*bh + mult3_sign_a = sign_a; + mult3_sign_b = sign_b; + mult3_op_a = op_a_i[`OP_H]; + mult3_op_b = op_b_i[`OP_H]; + mac_res_d = mac_res; + + summand1 = '0; + summand2 = accum; + summand3 = mult3_res; + + mult_state_d = MULL; + mult_valid = 1'b1; + + mult_hold = ~multdiv_ready_id_i; + end + + default: begin + mult_state_d = MULL; + end + + endcase // mult_state_q + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + mult_state_q <= MULL; + end else begin + if (mult_en_internal) begin + mult_state_q <= mult_state_d; + end + end + end + + assign unused_mult1_res_uns = mult1_res_uns[33:32]; + + // States must be knwon/valid. + + + // The fast multiplier uses one 17 bit multiplier to compute MUL instructions in 3 cycles + // and MULH instructions in 4 cycles. + end else begin : gen_mult_fast + logic [15:0] mult_op_a; + logic [15:0] mult_op_b; + + typedef enum logic [1:0] { + ALBL, ALBH, AHBL, AHBH + } mult_fsm_e; + mult_fsm_e mult_state_q, mult_state_d; + + // The 2 MSBs of mac_res_ext (mac_res_ext[34:33]) are always equal since: + // 1. The 2 MSBs of the multiplicants are always equal, and + // 2. The 16 MSBs of the addend (accum[33:18]) are always equal. + // Thus, it is safe to ignore mac_res_ext[34]. + assign mac_res_signed = + $signed({sign_a, mult_op_a}) * $signed({sign_b, mult_op_b}) + $signed(accum); + assign mac_res_ext = $unsigned(mac_res_signed); + assign mac_res = mac_res_ext[33:0]; + + always_comb begin + mult_op_a = op_a_i[`OP_L]; + mult_op_b = op_b_i[`OP_L]; + sign_a = 1'b0; + sign_b = 1'b0; + accum = imd_val_q_i[0]; + mac_res_d = mac_res; + mult_state_d = mult_state_q; + mult_valid = 1'b0; + mult_hold = 1'b0; + + unique case (mult_state_q) + + ALBL: begin + // al*bl + mult_op_a = op_a_i[`OP_L]; + mult_op_b = op_b_i[`OP_L]; + sign_a = 1'b0; + sign_b = 1'b0; + accum = '0; + mac_res_d = mac_res; + mult_state_d = ALBH; + end + + ALBH: begin + // al*bh<<16 + mult_op_a = op_a_i[`OP_L]; + mult_op_b = op_b_i[`OP_H]; + sign_a = 1'b0; + sign_b = signed_mode_i[1] & op_b_i[31]; + // result of AL*BL (in imd_val_q_i[0]) always unsigned with no carry + accum = {18'b0, imd_val_q_i[0][31:16]}; + if (operator_i == MD_OP_MULL) begin + mac_res_d = {2'b0, mac_res[`OP_L], imd_val_q_i[0][`OP_L]}; + end else begin + // MD_OP_MULH + mac_res_d = mac_res; + end + mult_state_d = AHBL; + end + + AHBL: begin + // ah*bl<<16 + mult_op_a = op_a_i[`OP_H]; + mult_op_b = op_b_i[`OP_L]; + sign_a = signed_mode_i[0] & op_a_i[31]; + sign_b = 1'b0; + if (operator_i == MD_OP_MULL) begin + accum = {18'b0, imd_val_q_i[0][31:16]}; + mac_res_d = {2'b0, mac_res[15:0], imd_val_q_i[0][15:0]}; + mult_valid = 1'b1; + + // Note no state transition will occur if mult_hold is set + mult_state_d = ALBL; + mult_hold = ~multdiv_ready_id_i; + end else begin + accum = imd_val_q_i[0]; + mac_res_d = mac_res; + mult_state_d = AHBH; + end + end + + AHBH: begin + // only MD_OP_MULH here + // ah*bh + mult_op_a = op_a_i[`OP_H]; + mult_op_b = op_b_i[`OP_H]; + sign_a = signed_mode_i[0] & op_a_i[31]; + sign_b = signed_mode_i[1] & op_b_i[31]; + accum[17: 0] = imd_val_q_i[0][33:16]; + accum[33:18] = {16{signed_mult & imd_val_q_i[0][33]}}; + // result of AH*BL is not signed only if signed_mode_i == 2'b00 + mac_res_d = mac_res; + mult_valid = 1'b1; + + // Note no state transition will occur if mult_hold is set + mult_state_d = ALBL; + mult_hold = ~multdiv_ready_id_i; + end + default: begin + mult_state_d = ALBL; + end + endcase // mult_state_q + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + mult_state_q <= ALBL; + end else begin + if (mult_en_internal) begin + mult_state_q <= mult_state_d; + end + end + end + + // States must be knwon/valid. + + end // gen_mult_fast + + // Divider + assign res_adder_h = alu_adder_ext_i[32:1]; + logic [1:0] unused_alu_adder_ext; + assign unused_alu_adder_ext = {alu_adder_ext_i[33],alu_adder_ext_i[0]}; + + assign next_remainder = is_greater_equal ? res_adder_h[31:0] : imd_val_q_i[0][31:0]; + assign next_quotient = is_greater_equal ? {1'b0, op_quotient_q} | {1'b0, one_shift} : + {1'b0, op_quotient_q}; + + assign one_shift = {31'b0, 1'b1} << div_counter_q; + + // The adder in the ALU computes alu_operand_a_o + alu_operand_b_o which means + // Remainder - Divisor. If Remainder - Divisor >= 0, is_greater_equal is equal to 1, + // the next Remainder is Remainder - Divisor contained in res_adder_h and the + always_comb begin + if ((imd_val_q_i[0][31] ^ op_denominator_q[31]) == 1'b0) begin + is_greater_equal = (res_adder_h[31] == 1'b0); + end else begin + is_greater_equal = imd_val_q_i[0][31]; + end + end + + assign div_sign_a = op_a_i[31] & signed_mode_i[0]; + assign div_sign_b = op_b_i[31] & signed_mode_i[1]; + assign div_change_sign = (div_sign_a ^ div_sign_b) & ~div_by_zero_q; + assign rem_change_sign = div_sign_a; + + + always_comb begin + div_counter_d = div_counter_q - 5'h1; + op_remainder_d = imd_val_q_i[0]; + op_quotient_d = op_quotient_q; + md_state_d = md_state_q; + op_numerator_d = op_numerator_q; + op_denominator_d = op_denominator_q; + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_b_i, 1'b1}; + div_valid = 1'b0; + div_hold = 1'b0; + div_by_zero_d = div_by_zero_q; + + unique case(md_state_q) + MD_IDLE: begin + if (operator_i == MD_OP_DIV) begin + // Check if the Denominator is 0 + // quotient for division by 0 is specified to be -1 + // Note with data-independent time option, the full divide operation will proceed as + // normal and will naturally return -1 + op_remainder_d = '1; + md_state_d = (!data_ind_timing_i && equal_to_zero_i) ? MD_FINISH : MD_ABS_A; + // Record that this is a div by zero to stop the sign change at the end of the + // division (in data_ind_timing mode). + div_by_zero_d = equal_to_zero_i; + end else begin + // Check if the Denominator is 0 + // remainder for division by 0 is specified to be the numerator (operand a) + // Note with data-independent time option, the full divide operation will proceed as + // normal and will naturally return operand a + op_remainder_d = {2'b0, op_a_i}; + md_state_d = (!data_ind_timing_i && equal_to_zero_i) ? MD_FINISH : MD_ABS_A; + end + // 0 - B = 0 iff B == 0 + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_b_i, 1'b1}; + div_counter_d = 5'd31; + end + + MD_ABS_A: begin + // quotient + op_quotient_d = '0; + // A abs value + op_numerator_d = div_sign_a ? alu_adder_i : op_a_i; + md_state_d = MD_ABS_B; + div_counter_d = 5'd31; + // ABS(A) = 0 - A + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_a_i, 1'b1}; + end + + MD_ABS_B: begin + // remainder + op_remainder_d = { 33'h0, op_numerator_q[31]}; + // B abs value + op_denominator_d = div_sign_b ? alu_adder_i : op_b_i; + md_state_d = MD_COMP; + div_counter_d = 5'd31; + // ABS(B) = 0 - B + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_b_i, 1'b1}; + end + + MD_COMP: begin + op_remainder_d = {1'b0, next_remainder[31:0], op_numerator_q[div_counter_d]}; + op_quotient_d = next_quotient[31:0]; + md_state_d = (div_counter_q == 5'd1) ? MD_LAST : MD_COMP; + // Division + alu_operand_a_o = {imd_val_q_i[0][31:0], 1'b1}; // it contains the remainder + alu_operand_b_o = {~op_denominator_q[31:0], 1'b1}; // -denominator two's compliment + end + + MD_LAST: begin + if (operator_i == MD_OP_DIV) begin + // this time we save the quotient in op_remainder_d (i.e. imd_val_q_i[0]) since + // we do not need anymore the remainder + op_remainder_d = {1'b0, next_quotient}; + end else begin + // this time we do not save the quotient anymore since we need only the remainder + op_remainder_d = {2'b0, next_remainder[31:0]}; + end + // Division + alu_operand_a_o = {imd_val_q_i[0][31:0], 1'b1}; // it contains the remainder + alu_operand_b_o = {~op_denominator_q[31:0], 1'b1}; // -denominator two's compliment + + md_state_d = MD_CHANGE_SIGN; + end + + MD_CHANGE_SIGN: begin + md_state_d = MD_FINISH; + if (operator_i == MD_OP_DIV) begin + op_remainder_d = (div_change_sign) ? {2'h0, alu_adder_i} : imd_val_q_i[0]; + end else begin + op_remainder_d = (rem_change_sign) ? {2'h0, alu_adder_i} : imd_val_q_i[0]; + end + // ABS(Quotient) = 0 - Quotient (or Remainder) + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~imd_val_q_i[0][31:0], 1'b1}; + end + + MD_FINISH: begin + // Hold result until ID stage is ready to accept it + // Note no state transition will occur if div_hold is set + md_state_d = MD_IDLE; + div_hold = ~multdiv_ready_id_i; + div_valid = 1'b1; + end + + default: begin + md_state_d = MD_IDLE; + end + endcase // md_state_q + end + + assign valid_o = mult_valid | div_valid; + + +endmodule // brq_mult
diff --git a/verilog/rtl/brq_exu_multdiv_slow.sv b/verilog/rtl/brq_exu_multdiv_slow.sv new file mode 100644 index 0000000..5592cb6 --- /dev/null +++ b/verilog/rtl/brq_exu_multdiv_slow.sv
@@ -0,0 +1,356 @@ + + +/** + * Slow Multiplier and Division + * + * Baugh-Wooley multiplier and Long Division + */ + + +module brq_exu_multdiv_slow +( + input logic clk_i, + input logic rst_ni, + input logic mult_en_i, // dynamic enable signal, for FSM control + input logic div_en_i, // dynamic enable signal, for FSM control + input logic mult_sel_i, // static decoder output, for data muxes + input logic div_sel_i, // static decoder output, for data muxes + input brq_pkg::md_op_e operator_i, + input logic [1:0] signed_mode_i, + input logic [31:0] op_a_i, + input logic [31:0] op_b_i, + input logic [33:0] alu_adder_ext_i, + input logic [31:0] alu_adder_i, + input logic equal_to_zero_i, + input logic data_ind_timing_i, + + output logic [32:0] alu_operand_a_o, + output logic [32:0] alu_operand_b_o, + + input logic [33:0] imd_val_q_i[2], + output logic [33:0] imd_val_d_o[2], + output logic [1:0] imd_val_we_o, + + input logic multdiv_ready_id_i, + + output logic [31:0] multdiv_result_o, + + output logic valid_o +); + + import brq_pkg::*; + + typedef enum logic [2:0] { + MD_IDLE, MD_ABS_A, MD_ABS_B, MD_COMP, MD_LAST, MD_CHANGE_SIGN, MD_FINISH + } md_fsm_e; + md_fsm_e md_state_q, md_state_d; + + logic [32:0] accum_window_q, accum_window_d; + logic unused_imd_val0; + logic [ 1:0] unused_imd_val1; + + logic [32:0] res_adder_l; + logic [32:0] res_adder_h; + + logic [ 4:0] multdiv_count_q, multdiv_count_d; + logic [32:0] op_b_shift_q, op_b_shift_d; + logic [32:0] op_a_shift_q, op_a_shift_d; + logic [32:0] op_a_ext, op_b_ext; + logic [32:0] one_shift; + logic [32:0] op_a_bw_pp, op_a_bw_last_pp; + logic [31:0] b_0; + logic sign_a, sign_b; + logic [32:0] next_quotient; + logic [31:0] next_remainder; + logic [31:0] op_numerator_q, op_numerator_d; + logic is_greater_equal; + logic div_change_sign, rem_change_sign; + logic div_by_zero_d, div_by_zero_q; + logic multdiv_hold; + logic multdiv_en; + + // (accum_window_q + op_a_shift_q) + assign res_adder_l = alu_adder_ext_i[32:0]; + // (accum_window_q + op_a_shift_q)>>1 + assign res_adder_h = alu_adder_ext_i[33:1]; + + ///////////////////// + // ALU Operand MUX // + ///////////////////// + + // Intermediate value register shared with ALU + assign imd_val_d_o[0] = {1'b0,accum_window_d}; + assign imd_val_we_o[0] = ~multdiv_hold; + assign accum_window_q = imd_val_q_i[0][32:0]; + assign unused_imd_val0 = imd_val_q_i[0][33]; + + assign imd_val_d_o[1] = {2'b00, op_numerator_d}; + assign imd_val_we_o[1] = multdiv_en; + assign op_numerator_q = imd_val_q_i[1][31:0]; + assign unused_imd_val1 = imd_val_q_i[1][33:32]; + + always_comb begin + alu_operand_a_o = accum_window_q; + + unique case(operator_i) + + MD_OP_MULL: begin + alu_operand_b_o = op_a_bw_pp; + end + + MD_OP_MULH: begin + alu_operand_b_o = (md_state_q == MD_LAST) ? op_a_bw_last_pp : op_a_bw_pp; + end + + MD_OP_DIV, + MD_OP_REM: begin + unique case(md_state_q) + MD_IDLE: begin + // 0 - B = 0 iff B == 0 + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_b_i, 1'b1}; + end + MD_ABS_A: begin + // ABS(A) = 0 - A + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_a_i, 1'b1}; + end + MD_ABS_B: begin + // ABS(B) = 0 - B + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~op_b_i, 1'b1}; + end + MD_CHANGE_SIGN: begin + // ABS(Quotient) = 0 - Quotient (or Reminder) + alu_operand_a_o = {32'h0 , 1'b1}; + alu_operand_b_o = {~accum_window_q[31:0], 1'b1}; + end + default: begin + // Division + alu_operand_a_o = {accum_window_q[31:0], 1'b1}; // it contains the remainder + alu_operand_b_o = {~op_b_shift_q[31:0], 1'b1}; // -denominator two's compliment + end + endcase + end + //default: begin + // alu_operand_a_o = accum_window_q; + // alu_operand_b_o = {~op_b_shift_q[31:0], 1'b1}; + // end + endcase + end + + // Multiplier partial product calculation + assign b_0 = {32{op_b_shift_q[0]}}; + assign op_a_bw_pp = { ~(op_a_shift_q[32] & op_b_shift_q[0]), (op_a_shift_q[31:0] & b_0) }; + assign op_a_bw_last_pp = { (op_a_shift_q[32] & op_b_shift_q[0]), ~(op_a_shift_q[31:0] & b_0) }; + + // Sign extend the input operands + assign sign_a = op_a_i[31] & signed_mode_i[0]; + assign sign_b = op_b_i[31] & signed_mode_i[1]; + + assign op_a_ext = {sign_a, op_a_i}; + assign op_b_ext = {sign_b, op_b_i}; + + // Divider calculations + + // The adder in the ALU computes Remainder - Divisor. If Remainder - Divisor >= 0, + // is_greater_equal is true, the next Remainder is the subtraction result and the Quotient + // multdiv_count_q-th bit is set to 1. + assign is_greater_equal = (accum_window_q[31] == op_b_shift_q[31]) ? + ~res_adder_h[31] : accum_window_q[31]; + + assign one_shift = {32'b0, 1'b1} << multdiv_count_q; + + assign next_remainder = is_greater_equal ? res_adder_h[31:0] : accum_window_q[31:0]; + assign next_quotient = is_greater_equal ? op_a_shift_q | one_shift : op_a_shift_q; + + assign div_change_sign = (sign_a ^ sign_b) & ~div_by_zero_q; + assign rem_change_sign = sign_a; + + always_comb begin + multdiv_count_d = multdiv_count_q; + accum_window_d = accum_window_q; + op_b_shift_d = op_b_shift_q; + op_a_shift_d = op_a_shift_q; + op_numerator_d = op_numerator_q; + md_state_d = md_state_q; + multdiv_hold = 1'b0; + div_by_zero_d = div_by_zero_q; + if (mult_sel_i || div_sel_i) begin + unique case(md_state_q) + MD_IDLE: begin + unique case(operator_i) + MD_OP_MULL: begin + op_a_shift_d = op_a_ext << 1; + accum_window_d = { ~(op_a_ext[32] & op_b_i[0]), + op_a_ext[31:0] & {32{op_b_i[0]}} }; + op_b_shift_d = op_b_ext >> 1; + // Proceed with multiplication by 0/1 in data-independent time mode + md_state_d = (!data_ind_timing_i && ((op_b_ext >> 1) == 0)) ? MD_LAST : MD_COMP; + end + MD_OP_MULH: begin + op_a_shift_d = op_a_ext; + accum_window_d = { 1'b1, ~(op_a_ext[32] & op_b_i[0]), + op_a_ext[31:1] & {31{op_b_i[0]}} }; + op_b_shift_d = op_b_ext >> 1; + md_state_d = MD_COMP; + end + MD_OP_DIV: begin + // Check if the denominator is 0 + // quotient for division by 0 is specified to be -1 + // Note with data-independent time option, the full divide operation will proceed as + // normal and will naturally return -1 + accum_window_d = {33{1'b1}}; + md_state_d = (!data_ind_timing_i && equal_to_zero_i) ? MD_FINISH : MD_ABS_A; + // Record that this is a div by zero to stop the sign change at the end of the + // division (in data_ind_timing mode). + div_by_zero_d = equal_to_zero_i; + end + MD_OP_REM: begin + // Check if the denominator is 0 + // remainder for division by 0 is specified to be the numerator (operand a) + // Note with data-independent time option, the full divide operation will proceed as + // normal and will naturally return operand a + accum_window_d = op_a_ext; + md_state_d = (!data_ind_timing_i && equal_to_zero_i) ? MD_FINISH : MD_ABS_A; + end + // default:; + endcase + multdiv_count_d = 5'd31; + end + + MD_ABS_A: begin + // quotient + op_a_shift_d = '0; + // A abs value + op_numerator_d = sign_a ? alu_adder_i : op_a_i; + md_state_d = MD_ABS_B; + end + + MD_ABS_B: begin + // remainder + accum_window_d = {32'h0,op_numerator_q[31]}; + // B abs value + op_b_shift_d = sign_b ? {1'b0,alu_adder_i} : {1'b0,op_b_i}; + md_state_d = MD_COMP; + end + + MD_COMP: begin + multdiv_count_d = multdiv_count_q - 5'h1; + unique case(operator_i) + MD_OP_MULL: begin + accum_window_d = res_adder_l; + op_a_shift_d = op_a_shift_q << 1; + op_b_shift_d = op_b_shift_q >> 1; + // Multiplication is complete once op_b is zero, unless in data_ind_timing mode where + // the maximum possible shift-add operations will be completed regardless of op_b + md_state_d = ((!data_ind_timing_i && (op_b_shift_d == 0)) || + (multdiv_count_q == 5'd1)) ? MD_LAST : MD_COMP; + end + MD_OP_MULH: begin + accum_window_d = res_adder_h; + op_a_shift_d = op_a_shift_q; + op_b_shift_d = op_b_shift_q >> 1; + md_state_d = (multdiv_count_q == 5'd1) ? MD_LAST : MD_COMP; + end + MD_OP_DIV, + MD_OP_REM: begin + accum_window_d = {next_remainder[31:0], op_numerator_q[multdiv_count_d]}; + op_a_shift_d = next_quotient; + md_state_d = (multdiv_count_q == 5'd1) ? MD_LAST : MD_COMP; + end + // default: ; + endcase + end + + MD_LAST: begin + unique case(operator_i) + MD_OP_MULL: begin + accum_window_d = res_adder_l; + + // Note no state transition will occur if multdiv_hold is set + md_state_d = MD_IDLE; + multdiv_hold = ~multdiv_ready_id_i; + end + MD_OP_MULH: begin + accum_window_d = res_adder_l; + md_state_d = MD_IDLE; + + // Note no state transition will occur if multdiv_hold is set + md_state_d = MD_IDLE; + multdiv_hold = ~multdiv_ready_id_i; + end + MD_OP_DIV: begin + // this time we save the quotient in accum_window_q since we do not need anymore the + // remainder + accum_window_d = next_quotient; + md_state_d = MD_CHANGE_SIGN; + end + MD_OP_REM: begin + // this time we do not save the quotient anymore since we need only the remainder + accum_window_d = {1'b0, next_remainder[31:0]}; + md_state_d = MD_CHANGE_SIGN; + end + // default: ; + endcase + end + + MD_CHANGE_SIGN: begin + md_state_d = MD_FINISH; + unique case(operator_i) + MD_OP_DIV: + accum_window_d = div_change_sign ? {1'b0,alu_adder_i} : accum_window_q; + MD_OP_REM: + accum_window_d = rem_change_sign ? {1'b0,alu_adder_i} : accum_window_q; + default: ; + endcase + end + + MD_FINISH: begin + // Note no state transition will occur if multdiv_hold is set + md_state_d = MD_IDLE; + multdiv_hold = ~multdiv_ready_id_i; + end + + default: begin + md_state_d = MD_IDLE; + end + endcase // md_state_q + end // (mult_sel_i || div_sel_i) + end + + ////////////////////////////////////////// + // Mutliplier / Divider state registers // + ////////////////////////////////////////// + + assign multdiv_en = (mult_en_i | div_en_i) & ~multdiv_hold; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + multdiv_count_q <= 5'h0; + op_b_shift_q <= 33'h0; + op_a_shift_q <= 33'h0; + md_state_q <= MD_IDLE; + div_by_zero_q <= 1'b0; + end else if (multdiv_en) begin + multdiv_count_q <= multdiv_count_d; + op_b_shift_q <= op_b_shift_d; + op_a_shift_q <= op_a_shift_d; + md_state_q <= md_state_d; + div_by_zero_q <= div_by_zero_d; + end + end + + ///////////// + // Outputs // + ///////////// + + assign valid_o = (md_state_q == MD_FINISH) | + (md_state_q == MD_LAST & + (operator_i == MD_OP_MULL | + operator_i == MD_OP_MULH)); + + assign multdiv_result_o = div_en_i ? accum_window_q[31:0] : res_adder_l[31:0]; + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_fp_register_file_ff.sv b/verilog/rtl/brq_fp_register_file_ff.sv new file mode 100644 index 0000000..3b1b597 --- /dev/null +++ b/verilog/rtl/brq_fp_register_file_ff.sv
@@ -0,0 +1,77 @@ +//`timescale 1ns/1ps +// 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 + +/** + * RISC-V register file + * + * Register file with 31 or 15x 32 bit wide registers. Register 0 is fixed to 0. + * This register file is based on flip flops. Use this register file when + * targeting FPGA synthesis or Verilator simulation. + */ + +module brq_fp_register_file_ff #( + parameter brq_pkg::rvfloat_e RVF = brq_pkg::RV32FSingle, + parameter int unsigned DataWidth = 32 + ) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + //Read port R1 + input logic [4:0] raddr_a_i, + output logic [DataWidth-1:0] rdata_a_o, + + //Read port R2 + input logic [4:0] raddr_b_i, + output logic [DataWidth-1:0] rdata_b_o, + + //Read port R2 + input logic [4:0] raddr_c_i, + output logic [DataWidth-1:0] rdata_c_o, + + + // Write port W1 + input logic [4:0] waddr_a_i, + input logic [DataWidth-1:0] wdata_a_i, + input logic we_a_i + +); +import brq_pkg::rvfloat_e; + + localparam int unsigned ADDR_WIDTH = (RVF==brq_pkg::RV64FDouble) ? 6 : 5; + localparam int unsigned NUM_WORDS = (RVF==brq_pkg::RV64FDouble) ? 64 : 32; + + logic [NUM_WORDS-1:0][DataWidth-1:0] rf_reg; + logic [NUM_WORDS-1:0][DataWidth-1:0] rf_reg_q; + logic [NUM_WORDS-1:0] we_a_dec; + + always_comb begin : we_a_decoder + for (int unsigned i = 0; i < NUM_WORDS; i++) begin + we_a_dec[i] = (waddr_a_i == 5'(i)) ? we_a_i : 1'b0; + end + end + + for (genvar i = 0; i < NUM_WORDS; i++) begin : g_rf_flops + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rf_reg_q[i] <= '0; + end else if(we_a_dec[i]) begin + rf_reg_q[i] <= wdata_a_i; + end +// else begin +// rf_reg_q[5] <= 32'h41a00000; +// rf_reg_q[6] <= 32'h41200000; +// end + end + end + + assign rf_reg[NUM_WORDS-1:0] = rf_reg_q[NUM_WORDS-1:0]; + + assign rdata_a_o = rf_reg[raddr_a_i]; + assign rdata_b_o = rf_reg[raddr_b_i]; + assign rdata_c_o = rf_reg[raddr_c_i]; + +endmodule
diff --git a/verilog/rtl/brq_idu.sv b/verilog/rtl/brq_idu.sv new file mode 100644 index 0000000..df82b18 --- /dev/null +++ b/verilog/rtl/brq_idu.sv
@@ -0,0 +1,1077 @@ + + +`ifdef RISCV_FORMAL + `define RVFI +`endif + +/** + * Instruction Decode Stage + * + * Decode stage of the core. It decodes the instructions and hosts the register + * file. + */ + +module brq_idu #( + parameter bit RV32E = 0, + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, + parameter brq_pkg::rv32b_e RV32B = brq_pkg::RV32BNone, + parameter brq_pkg::rvfloat_e RVF = brq_pkg::RV64FDouble, + parameter bit DataIndTiming = 1'b0, + parameter bit BranchTargetALU = 0, + parameter bit SpecBranch = 0, + parameter bit WritebackStage = 0, + parameter bit BranchPredictor = 0 +) ( + input logic clk_i, + input logic rst_ni, + + output logic ctrl_busy_o, + output logic illegal_insn_o, + + // Interface to IF stage + input logic instr_valid_i, + input logic [31:0] instr_rdata_i, // from IF-ID pipeline registers + input logic [31:0] instr_rdata_alu_i, // from IF-ID pipeline registers + input logic [15:0] instr_rdata_c_i, // from IF-ID pipeline registers + input logic instr_is_compressed_i, + input logic instr_bp_taken_i, + output logic instr_req_o, + output logic instr_first_cycle_id_o, + output logic instr_valid_clear_o, // kill instr in IF-ID reg + output logic id_in_ready_o, // ID stage is ready for next instr + output logic icache_inval_o, + + // Jumps and branches + input logic branch_decision_i, + + // IF and ID stage signals + output logic pc_set_o, + output logic pc_set_spec_o, + output brq_pkg::pc_sel_e pc_mux_o, + output logic nt_branch_mispredict_o, + output brq_pkg::exc_pc_sel_e exc_pc_mux_o, + output brq_pkg::exc_cause_e exc_cause_o, + + input logic illegal_c_insn_i, + input logic instr_fetch_err_i, + input logic instr_fetch_err_plus2_i, + + input logic [31:0] pc_id_i, + + // Stalls + input logic ex_valid_i, // EX stage has valid output + input logic lsu_resp_valid_i, // LSU has valid output, or is done + // ALU + output brq_pkg::alu_op_e alu_operator_ex_o, + output logic [31:0] alu_operand_a_ex_o, + output logic [31:0] alu_operand_b_ex_o, + + // Multicycle Operation Stage Register + input logic [1:0] imd_val_we_ex_i, + input logic [33:0] imd_val_d_ex_i[2], + output logic [33:0] imd_val_q_ex_o[2], + + // Branch target ALU + output logic [31:0] bt_a_operand_o, + output logic [31:0] bt_b_operand_o, + + // MUL, DIV + output logic mult_en_ex_o, + output logic div_en_ex_o, + output logic mult_sel_ex_o, + output logic div_sel_ex_o, + output brq_pkg::md_op_e multdiv_operator_ex_o, + output logic [1:0] multdiv_signed_mode_ex_o, + output logic [31:0] multdiv_operand_a_ex_o, + output logic [31:0] multdiv_operand_b_ex_o, + output logic multdiv_ready_id_o, + + // CSR + output logic csr_access_o, + output brq_pkg::csr_op_e csr_op_o, + output logic csr_op_en_o, + output logic csr_save_if_o, + output logic csr_save_id_o, + output logic csr_save_wb_o, + output logic csr_restore_mret_id_o, + output logic csr_restore_dret_id_o, + output logic csr_save_cause_o, + output logic [31:0] csr_mtval_o, + input brq_pkg::priv_lvl_e priv_mode_i, + input logic csr_mstatus_tw_i, + input logic illegal_csr_insn_i, + input logic data_ind_timing_i, + + // Interface to load store unit + output logic lsu_req_o, + output logic lsu_we_o, + output logic [1:0] lsu_type_o, + output logic lsu_sign_ext_o, + output logic [31:0] lsu_wdata_o, + + input logic lsu_req_done_i, // Data req to LSU is complete and + // instruction can move to writeback + // (only relevant where writeback stage is + // present) + + input logic lsu_addr_incr_req_i, + input logic [31:0] lsu_addr_last_i, + + // Interrupt signals + input logic csr_mstatus_mie_i, + input logic irq_pending_i, + input brq_pkg::irqs_t irqs_i, + input logic irq_nm_i, + output logic nmi_mode_o, + + input logic lsu_load_err_i, + input logic lsu_store_err_i, + + // Debug Signal + output logic debug_mode_o, + output brq_pkg::dbg_cause_e debug_cause_o, + output logic debug_csr_save_o, + input logic debug_req_i, + input logic debug_single_step_i, + input logic debug_ebreakm_i, + input logic debug_ebreaku_i, + input logic trigger_match_i, + + // Write back signal + input logic [31:0] result_ex_i, + input logic [31:0] csr_rdata_i, + + // Register file read + output logic [4:0] rf_raddr_a_o, + input logic [31:0] rf_rdata_a_i, + output logic [4:0] rf_raddr_b_o, + input logic [31:0] rf_rdata_b_i, + output logic rf_ren_a_o, + output logic rf_ren_b_o, + + // Register file write (via writeback) + output logic [4:0] rf_waddr_id_o, + output logic [31:0] rf_wdata_id_o, + output logic rf_we_id_o, + output logic rf_rd_a_wb_match_o, + output logic rf_rd_b_wb_match_o, + + // Register write information from writeback (for resolving data hazards) + input logic [4:0] rf_waddr_wb_i, + input logic [31:0] rf_wdata_fwd_wb_i, + input logic rf_write_wb_i, + + output logic en_wb_o, + output brq_pkg::wb_instr_type_e instr_type_wb_o, + output logic instr_perf_count_id_o, + input logic ready_wb_i, + input logic outstanding_load_wb_i, + input logic outstanding_store_wb_i, + + // Performance Counters + output logic perf_jump_o, // executing a jump instr + output logic perf_branch_o, // executing a branch instr + output logic perf_tbranch_o, // executing a taken branch instr + output logic perf_dside_wait_o, // instruction in ID/EX is awaiting memory + // access to finish before proceeding + output logic perf_mul_wait_o, + output logic perf_div_wait_o, + output logic instr_id_done_o, + + // Floating point extensions IO + output fpnew_pkg::roundmode_e fp_rounding_mode_o, // defines the rounding mode + // output brq_pkg::op_b_sel_e fp_alu_op_b_mux_sel_o, // operand b selection: reg value or + // immediate + input logic [31:0] fp_rf_rdata_a_i, + input logic [31:0] fp_rf_rdata_b_i, + input logic [31:0] fp_rf_rdata_c_i, + output logic [4:0] fp_rf_raddr_a_o, + output logic [4:0] fp_rf_raddr_b_o, + output logic [4:0] fp_rf_raddr_c_o, + //output logic fp_rf_ren_a_o, + //output logic fp_rf_ren_b_o, + //output logic fp_rf_ren_c_o, + output logic [4:0] fp_rf_waddr_o, + output logic fp_rf_we_o, + + output fpnew_pkg::operation_e fp_alu_operator_o, + output logic fp_alu_op_mod_o, + output fpnew_pkg::fp_format_e fp_src_fmt_o, + output fpnew_pkg::fp_format_e fp_dst_fmt_o, + output logic fp_rm_dynamic_o, + output logic fp_flush_o, + output logic is_fp_instr_o, + output logic use_fp_rs1_o, + output logic use_fp_rs2_o, + output logic use_fp_rs3_o, + output logic use_fp_rd_o, + input logic fpu_busy_i, + input logic fp_rf_write_wb_i, + input logic [31:0] fp_rf_wdata_fwd_wb_i, + output logic [2:0][31:0] fp_operands_o, + output logic fp_load_o +); + + import brq_pkg::*; + + // Decoder/Controller, ID stage internal signals + logic illegal_insn_dec; + logic ebrk_insn; + logic mret_insn_dec; + logic dret_insn_dec; + logic ecall_insn_dec; + logic wfi_insn_dec; + + logic wb_exception; + + logic branch_in_dec; + logic branch_spec, branch_set_spec; + logic branch_set, branch_set_d; + logic branch_not_set; + logic branch_taken; + logic jump_in_dec; + logic jump_set_dec; + logic jump_set; + + logic instr_first_cycle; + logic instr_executing; + logic instr_done; + logic controller_run; + logic stall_ld_hz; + logic stall_mem; + logic stall_multdiv; + logic stall_branch; + logic stall_jump; + logic stall_id; + logic stall_wb; + logic flush_id; + logic multicycle_done; + + // Immediate decoding and sign extension + logic [31:0] imm_i_type; + logic [31:0] imm_s_type; + logic [31:0] imm_b_type; + logic [31:0] imm_u_type; + logic [31:0] imm_j_type; + logic [31:0] zimm_rs1_type; + + logic [31:0] imm_a; // contains the immediate for operand b + logic [31:0] imm_b; // contains the immediate for operand b + + // Register file interface + + rf_wd_sel_e rf_wdata_sel; + logic rf_we_dec, rf_we_raw; + logic rf_ren_a, rf_ren_b; + + assign rf_ren_a_o = rf_ren_a; + assign rf_ren_b_o = rf_ren_b; + + logic [31:0] rf_rdata_a_fwd; + logic [31:0] rf_rdata_b_fwd; + + // ALU Control + alu_op_e alu_operator; + op_a_sel_e alu_op_a_mux_sel, alu_op_a_mux_sel_dec; + op_b_sel_e alu_op_b_mux_sel, alu_op_b_mux_sel_dec; + logic alu_multicycle_dec; + logic stall_alu; + + logic [33:0] imd_val_q[2]; + + op_a_sel_e bt_a_mux_sel; + imm_b_sel_e bt_b_mux_sel; + + imm_a_sel_e imm_a_mux_sel; + imm_b_sel_e imm_b_mux_sel, imm_b_mux_sel_dec; + + // Multiplier Control + logic mult_en_id, mult_en_dec; // use integer multiplier + logic div_en_id, div_en_dec; // use integer division or reminder + logic multdiv_en_dec; + md_op_e multdiv_operator; + logic [1:0] multdiv_signed_mode; + + // Data Memory Control + logic lsu_we; + logic [1:0] lsu_type; + logic lsu_sign_ext; + logic lsu_req, lsu_req_dec; + logic data_req_allowed; + + // CSR control + logic csr_pipe_flush; + + logic [31:0] alu_operand_a; + logic [31:0] alu_operand_b; + + // Floating point + logic fp_swap_oprnds; + logic [31:0] fp_rf_rdata_a_fwd; + logic [31:0] fp_rf_rdata_b_fwd; + logic [31:0] fp_rf_rdata_c_fwd; + logic [31:0] temp; + logic [31:0] fpu_op_a; + logic [31:0] fpu_op_b; + logic [31:0] fpu_op_c; + logic mv_instr; + logic [31:0] result_wb; + + ///////////// + // LSU Mux // + ///////////// + + // Misaligned loads/stores result in two aligned loads/stores, compute second address + assign alu_op_a_mux_sel = lsu_addr_incr_req_i ? OP_A_FWD : alu_op_a_mux_sel_dec; + assign alu_op_b_mux_sel = lsu_addr_incr_req_i ? OP_B_IMM : alu_op_b_mux_sel_dec; + assign imm_b_mux_sel = lsu_addr_incr_req_i ? IMM_B_INCR_ADDR : imm_b_mux_sel_dec; + + /////////////////// + // Operand MUXES // + /////////////////// + + // Main ALU immediate MUX for Operand A + assign imm_a = (imm_a_mux_sel == IMM_A_Z) ? zimm_rs1_type : '0; + + // Main ALU MUX for Operand A + always_comb begin : alu_operand_a_mux + unique case (alu_op_a_mux_sel) + OP_A_REG_A: alu_operand_a = rf_rdata_a_fwd; + OP_A_FWD: alu_operand_a = lsu_addr_last_i; + OP_A_CURRPC: alu_operand_a = pc_id_i; + OP_A_IMM: alu_operand_a = imm_a; + //default: alu_operand_a = pc_id_i; + endcase + end + + if (BranchTargetALU) begin : g_btalu_muxes + // Branch target ALU operand A mux + always_comb begin : bt_operand_a_mux + unique case (bt_a_mux_sel) + OP_A_REG_A: bt_a_operand_o = rf_rdata_a_fwd; + OP_A_CURRPC: bt_a_operand_o = pc_id_i; + default: bt_a_operand_o = pc_id_i; + endcase + end + + // Branch target ALU operand B mux + always_comb begin : bt_immediate_b_mux + unique case (bt_b_mux_sel) + IMM_B_I: bt_b_operand_o = imm_i_type; + IMM_B_B: bt_b_operand_o = imm_b_type; + IMM_B_J: bt_b_operand_o = imm_j_type; + IMM_B_INCR_PC: bt_b_operand_o = instr_is_compressed_i ? 32'h2 : 32'h4; + default: bt_b_operand_o = instr_is_compressed_i ? 32'h2 : 32'h4; + endcase + end + + // Reduced main ALU immediate MUX for Operand B + always_comb begin : immediate_b_mux + unique case (imm_b_mux_sel) + IMM_B_I: imm_b = imm_i_type; + IMM_B_S: imm_b = imm_s_type; + IMM_B_U: imm_b = imm_u_type; + IMM_B_INCR_PC: imm_b = instr_is_compressed_i ? 32'h2 : 32'h4; + IMM_B_INCR_ADDR: imm_b = 32'h4; + default: imm_b = 32'h4; + endcase + end + + end else begin : g_nobtalu + op_a_sel_e unused_a_mux_sel; + imm_b_sel_e unused_b_mux_sel; + + assign unused_a_mux_sel = bt_a_mux_sel; + assign unused_b_mux_sel = bt_b_mux_sel; + assign bt_a_operand_o = '0; + assign bt_b_operand_o = '0; + + // Full main ALU immediate MUX for Operand B + always_comb begin : immediate_b_mux + unique case (imm_b_mux_sel) + IMM_B_I: imm_b = imm_i_type; + IMM_B_S: imm_b = imm_s_type; + IMM_B_B: imm_b = imm_b_type; + IMM_B_U: imm_b = imm_u_type; + IMM_B_J: imm_b = imm_j_type; + IMM_B_INCR_PC: imm_b = instr_is_compressed_i ? 32'h2 : 32'h4; + IMM_B_INCR_ADDR: imm_b = 32'h4; + default: imm_b = 32'h4; + endcase + end + + end + + // ALU MUX for Operand B + assign alu_operand_b = (alu_op_b_mux_sel == OP_B_IMM) ? imm_b : rf_rdata_b_fwd; + + ///////////////////////////////////////// + // Multicycle Operation Stage Register // + ///////////////////////////////////////// + + for (genvar i=0; i<2; i++) begin : gen_intermediate_val_reg + always_ff @(posedge clk_i or negedge rst_ni) begin : intermediate_val_reg + if (!rst_ni) begin + imd_val_q[i] <= '0; + end else if (imd_val_we_ex_i[i]) begin + imd_val_q[i] <= imd_val_d_ex_i[i]; + end + end + end + + assign imd_val_q_ex_o = imd_val_q; + + ///////////// + // Decoder // + ///////////// + + brq_idu_decoder #( + .RV32E ( RV32E ), + .RV32M ( RV32M ), + .RV32B ( RV32B ), + .BranchTargetALU ( BranchTargetALU ) + ) decoder_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + // controller + .illegal_insn_o ( illegal_insn_dec ), + .ebrk_insn_o ( ebrk_insn ), + .mret_insn_o ( mret_insn_dec ), + .dret_insn_o ( dret_insn_dec ), + .ecall_insn_o ( ecall_insn_dec ), + .wfi_insn_o ( wfi_insn_dec ), + .jump_set_o ( jump_set_dec ), + .branch_taken_i ( branch_taken ), + .icache_inval_o ( icache_inval_o ), + + // from IF-ID pipeline register + .instr_first_cycle_i ( instr_first_cycle ), + .instr_rdata_i ( instr_rdata_i ), + .instr_rdata_alu_i ( instr_rdata_alu_i ), + .illegal_c_insn_i ( illegal_c_insn_i ), + + // immediates + .imm_a_mux_sel_o ( imm_a_mux_sel ), + .imm_b_mux_sel_o ( imm_b_mux_sel_dec ), + .bt_a_mux_sel_o ( bt_a_mux_sel ), + .bt_b_mux_sel_o ( bt_b_mux_sel ), + + .imm_i_type_o ( imm_i_type ), + .imm_s_type_o ( imm_s_type ), + .imm_b_type_o ( imm_b_type ), + .imm_u_type_o ( imm_u_type ), + .imm_j_type_o ( imm_j_type ), + .zimm_rs1_type_o ( zimm_rs1_type ), + + // register file + .rf_wdata_sel_o ( rf_wdata_sel ), + .rf_we_o ( rf_we_dec ), + + .rf_raddr_a_o ( rf_raddr_a_o ), + .rf_raddr_b_o ( rf_raddr_b_o ), + .rf_waddr_o ( rf_waddr_id_o ), + .rf_ren_a_o ( rf_ren_a ), + .rf_ren_b_o ( rf_ren_b ), + + // ALU + .alu_operator_o ( alu_operator ), + .alu_op_a_mux_sel_o ( alu_op_a_mux_sel_dec ), + .alu_op_b_mux_sel_o ( alu_op_b_mux_sel_dec ), + .alu_multicycle_o ( alu_multicycle_dec ), + + // MULT & DIV + .mult_en_o ( mult_en_dec ), + .div_en_o ( div_en_dec ), + .mult_sel_o ( mult_sel_ex_o ), + .div_sel_o ( div_sel_ex_o ), + .multdiv_operator_o ( multdiv_operator ), + .multdiv_signed_mode_o ( multdiv_signed_mode ), + + // CSRs + .csr_access_o ( csr_access_o ), + .csr_op_o ( csr_op_o ), + + // LSU + .data_req_o ( lsu_req_dec ), + .data_we_o ( lsu_we ), + .data_type_o ( lsu_type ), + .data_sign_extension_o ( lsu_sign_ext ), + + // jump/branches + .jump_in_dec_o ( jump_in_dec ), + .branch_in_dec_o ( branch_in_dec ), + + // Floating point extensions IO + .fp_rounding_mode_o ( fp_rounding_mode_o ), // defines the rounding mode + .fp_rf_raddr_a_o ( fp_rf_raddr_a_o ), + .fp_rf_raddr_b_o ( fp_rf_raddr_b_o ), + .fp_rf_raddr_c_o ( fp_rf_raddr_c_o ), + .fp_rf_waddr_o ( fp_rf_waddr_o ), + .fp_rf_we_o ( fp_rf_we_o ), + .fp_alu_operator_o ( fp_alu_operator_o ), + .fp_alu_op_mod_o ( fp_alu_op_mod_o ), + .fp_src_fmt_o ( fp_src_fmt_o ), + .fp_dst_fmt_o ( fp_dst_fmt_o ), + .fp_rm_dynamic_o ( fp_rm_dynamic_o ), + .is_fp_instr_o ( is_fp_instr_o ), + .use_fp_rs1_o ( use_fp_rs1_o ), + .use_fp_rs2_o ( use_fp_rs2_o ), + .use_fp_rs3_o ( use_fp_rs3_o ), + .use_fp_rd_o ( use_fp_rd_o ), + .fp_swap_oprnds_o ( fp_swap_oprnds ), + .fp_load_o ( fp_load_o ), + .mv_instr_o ( mv_instr ) + ); + +// assign fpu_op_a = use_fp_rs1_o ? fp_rf_rdata_a_fwd : rf_rdata_a_fwd; +// assign fpu_op_b = use_fp_rs2_o ? fp_rf_rdata_b_fwd : rf_rdata_b_fwd; +// assign fpu_op_c = fp_rf_rdata_c_fwd; + + /////////////////////// + // Register File MUX // + /////////////////////// + + // Suppress register write if there is an illegal CSR access or instruction is not executing + assign rf_we_id_o = rf_we_raw & instr_executing & ~illegal_csr_insn_i; + + // Register file write data mux + always_comb begin : rf_wdata_id_mux + unique case (rf_wdata_sel) + RF_WD_EX: rf_wdata_id_o = result_wb; + RF_WD_CSR: rf_wdata_id_o = csr_rdata_i; + // default: rf_wdata_id_o = result_wb; + endcase + end + + ///////////////////////////////// + // CSR-related pipline flushes // + ///////////////////////////////// + always_comb begin : csr_pipeline_flushes + csr_pipe_flush = 1'b0; + + // A pipeline flush is needed to let the controller react after modifying certain CSRs: + // - When enabling interrupts, pending IRQs become visible to the controller only during + // the next cycle. If during that cycle the core disables interrupts again, it does not + // see any pending IRQs and consequently does not start to handle interrupts. + // - When modifying debug CSRs - TODO: Check if this is really needed + if (csr_op_en_o == 1'b1 && (csr_op_o == CSR_OP_WRITE || csr_op_o == CSR_OP_SET)) begin + if (csr_num_e'(instr_rdata_i[31:20]) == CSR_MSTATUS || + csr_num_e'(instr_rdata_i[31:20]) == CSR_MIE) begin + csr_pipe_flush = 1'b1; + end + end else if (csr_op_en_o == 1'b1 && csr_op_o != CSR_OP_READ) begin + if (csr_num_e'(instr_rdata_i[31:20]) == CSR_DCSR || + csr_num_e'(instr_rdata_i[31:20]) == CSR_DPC || + csr_num_e'(instr_rdata_i[31:20]) == CSR_DSCRATCH0 || + csr_num_e'(instr_rdata_i[31:20]) == CSR_DSCRATCH1) begin + csr_pipe_flush = 1'b1; + end + end + end + + //////////////// + // Controller // + //////////////// + + assign illegal_insn_o = instr_valid_i & (illegal_insn_dec | illegal_csr_insn_i); + + brq_idu_controller #( + .WritebackStage ( WritebackStage ), + .BranchPredictor ( BranchPredictor ) + ) controller_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .ctrl_busy_o ( ctrl_busy_o ), + + // decoder related signals + .illegal_insn_i ( illegal_insn_o ), + .ecall_insn_i ( ecall_insn_dec ), + .mret_insn_i ( mret_insn_dec ), + .dret_insn_i ( dret_insn_dec ), + .wfi_insn_i ( wfi_insn_dec ), + .ebrk_insn_i ( ebrk_insn ), + .csr_pipe_flush_i ( csr_pipe_flush ), + + // from IF-ID pipeline + .instr_valid_i ( instr_valid_i ), + .instr_i ( instr_rdata_i ), + .instr_compressed_i ( instr_rdata_c_i ), + .instr_is_compressed_i ( instr_is_compressed_i ), + .instr_bp_taken_i ( instr_bp_taken_i ), + .instr_fetch_err_i ( instr_fetch_err_i ), + .instr_fetch_err_plus2_i ( instr_fetch_err_plus2_i ), + .pc_id_i ( pc_id_i ), + + // to IF-ID pipeline + .instr_valid_clear_o ( instr_valid_clear_o ), + .id_in_ready_o ( id_in_ready_o ), + .controller_run_o ( controller_run ), + + // to prefetcher + .instr_req_o ( instr_req_o ), + .pc_set_o ( pc_set_o ), + .pc_set_spec_o ( pc_set_spec_o ), + .pc_mux_o ( pc_mux_o ), + .nt_branch_mispredict_o ( nt_branch_mispredict_o ), + .exc_pc_mux_o ( exc_pc_mux_o ), + .exc_cause_o ( exc_cause_o ), + + // LSU + .lsu_addr_last_i ( lsu_addr_last_i ), + .load_err_i ( lsu_load_err_i ), + .store_err_i ( lsu_store_err_i ), + .wb_exception_o ( wb_exception ), + + // jump/branch control + .branch_set_i ( branch_set ), + .branch_set_spec_i ( branch_set_spec ), + .branch_not_set_i ( branch_not_set ), + .jump_set_i ( jump_set ), + + // interrupt signals + .csr_mstatus_mie_i ( csr_mstatus_mie_i ), + .irq_pending_i ( irq_pending_i ), + .irqs_i ( irqs_i ), + .irq_nm_i ( irq_nm_i ), + .nmi_mode_o ( nmi_mode_o ), + + // CSR Controller Signals + .csr_save_if_o ( csr_save_if_o ), + .csr_save_id_o ( csr_save_id_o ), + .csr_save_wb_o ( csr_save_wb_o ), + .csr_restore_mret_id_o ( csr_restore_mret_id_o ), + .csr_restore_dret_id_o ( csr_restore_dret_id_o ), + .csr_save_cause_o ( csr_save_cause_o ), + .csr_mtval_o ( csr_mtval_o ), + .priv_mode_i ( priv_mode_i ), + .csr_mstatus_tw_i ( csr_mstatus_tw_i ), + + // Debug Signal + .debug_mode_o ( debug_mode_o ), + .debug_cause_o ( debug_cause_o ), + .debug_csr_save_o ( debug_csr_save_o ), + .debug_req_i ( debug_req_i ), + .debug_single_step_i ( debug_single_step_i ), + .debug_ebreakm_i ( debug_ebreakm_i ), + .debug_ebreaku_i ( debug_ebreaku_i ), + .trigger_match_i ( trigger_match_i ), + + .stall_id_i ( stall_id ), + .stall_wb_i ( stall_wb ), + .flush_id_o ( flush_id ), + .ready_wb_i ( ready_wb_i ), + + // Performance Counters + .perf_jump_o ( perf_jump_o ), + .perf_tbranch_o ( perf_tbranch_o ), + .fpu_busy_i ( fpu_busy_i ) + ); + + assign fp_flush_o = flush_id; + assign multdiv_en_dec = mult_en_dec | div_en_dec; + + assign lsu_req = instr_executing ? data_req_allowed & lsu_req_dec : 1'b0; + assign mult_en_id = instr_executing ? mult_en_dec : 1'b0; + assign div_en_id = instr_executing ? div_en_dec : 1'b0; + + assign lsu_req_o = lsu_req; + assign lsu_we_o = lsu_we; + assign lsu_type_o = lsu_type; + assign lsu_sign_ext_o = lsu_sign_ext; + assign lsu_wdata_o = fpu_op_b; //rf_rdata_b_fwd; + // csr_op_en_o is set when CSR access should actually happen. + // csv_access_o is set when CSR access instruction is present and is used to compute whether a CSR + // access is illegal. A combinational loop would be created if csr_op_en_o was used along (as + // asserting it for an illegal csr access would result in a flush that would need to deassert it). + assign csr_op_en_o = csr_access_o & instr_executing & instr_id_done_o; + + assign alu_operator_ex_o = alu_operator; + assign alu_operand_a_ex_o = alu_operand_a; + assign alu_operand_b_ex_o = alu_operand_b; + + assign mult_en_ex_o = mult_en_id; + assign div_en_ex_o = div_en_id; + + assign multdiv_operator_ex_o = multdiv_operator; + assign multdiv_signed_mode_ex_o = multdiv_signed_mode; + assign multdiv_operand_a_ex_o = rf_rdata_a_fwd; + assign multdiv_operand_b_ex_o = rf_rdata_b_fwd; + + //////////////////////// + // Branch set control // + //////////////////////// + + if (BranchTargetALU && !DataIndTiming) begin : g_branch_set_direct + // Branch set fed straight to controller with branch target ALU + // (condition pass/fail used same cycle as generated instruction request) + assign branch_set = branch_set_d; + assign branch_set_spec = branch_spec; + end else begin : g_branch_set_flop + // Branch set flopped without branch target ALU, or in fixed time execution mode + // (condition pass/fail used next cycle where branch target is calculated) + logic branch_set_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + branch_set_q <= 1'b0; + end else begin + branch_set_q <= branch_set_d; + end + end + + // Branches always take two cycles in fixed time execution mode, with or without the branch + // target ALU (to avoid a path from the branch decision into the branch target ALU operand + // muxing). + assign branch_set = (BranchTargetALU && !data_ind_timing_i) ? branch_set_d : branch_set_q; + // Use the speculative branch signal when BTALU is enabled + assign branch_set_spec = (BranchTargetALU && !data_ind_timing_i) ? branch_spec : branch_set_q; + end + + // Branch condition is calculated in the first cycle and flopped for use in the second cycle + // (only used in fixed time execution mode to determine branch destination). + if (DataIndTiming) begin : g_sec_branch_taken + logic branch_taken_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + branch_taken_q <= 1'b0; + end else begin + branch_taken_q <= branch_decision_i; + end + end + + assign branch_taken = ~data_ind_timing_i | branch_taken_q; + + end else begin : g_nosec_branch_taken + + // Signal unused without fixed time execution mode - only taken branches will trigger branch_set + assign branch_taken = 1'b1; + + end + + // Holding branch_set/jump_set high for more than one cycle should not cause a functional issue. + // However it could generate needless prefetch buffer flushes and instruction fetches. The ID/EX + // designs ensures that this never happens for non-predicted branches. + + + /////////////// + // ID-EX FSM // + /////////////// + + typedef enum logic { FIRST_CYCLE, MULTI_CYCLE } id_fsm_e; + id_fsm_e id_fsm_q, id_fsm_d; + + always_ff @(posedge clk_i or negedge rst_ni) begin : id_pipeline_reg + if (!rst_ni) begin + id_fsm_q <= FIRST_CYCLE; + end else begin + id_fsm_q <= id_fsm_d; + end + end + + // ID/EX stage can be in two states, FIRST_CYCLE and MULTI_CYCLE. An instruction enters + // MULTI_CYCLE if it requires multiple cycles to complete regardless of stalls and other + // considerations. An instruction may be held in FIRST_CYCLE if it's unable to begin executing + // (this is controlled by instr_executing). + + always_comb begin + id_fsm_d = id_fsm_q; + rf_we_raw = rf_we_dec; + stall_multdiv = 1'b0; + stall_jump = 1'b0; + stall_branch = 1'b0; + stall_alu = 1'b0; + branch_set_d = 1'b0; + branch_spec = 1'b0; + branch_not_set = 1'b0; + jump_set = 1'b0; + perf_branch_o = 1'b0; + + if (instr_executing) begin + unique case (id_fsm_q) + FIRST_CYCLE: begin + unique case (1'b1) + lsu_req_dec: begin + if (!WritebackStage) begin + // LSU operation + id_fsm_d = MULTI_CYCLE; + end else begin + if(~lsu_req_done_i) begin + id_fsm_d = MULTI_CYCLE; + end + end + end + multdiv_en_dec: begin + // MUL or DIV operation + if (~ex_valid_i) begin + // When single-cycle multiply is configured mul can finish in the first cycle so + // only enter MULTI_CYCLE state if a result isn't immediately available + id_fsm_d = MULTI_CYCLE; + rf_we_raw = 1'b0; + stall_multdiv = 1'b1; + end + end + branch_in_dec: begin + // cond branch operation + // All branches take two cycles in fixed time execution mode, regardless of branch + // condition. + id_fsm_d = (data_ind_timing_i || (!BranchTargetALU && branch_decision_i)) ? + MULTI_CYCLE : FIRST_CYCLE; + stall_branch = (~BranchTargetALU & branch_decision_i) | data_ind_timing_i; + branch_set_d = branch_decision_i | data_ind_timing_i; + + if (BranchPredictor) begin + branch_not_set = ~branch_decision_i; + end + + // Speculative branch (excludes branch_decision_i) + branch_spec = SpecBranch ? 1'b1 : branch_decision_i; + perf_branch_o = 1'b1; + end + jump_in_dec: begin + // uncond branch operation + // BTALU means jumps only need one cycle + id_fsm_d = BranchTargetALU ? FIRST_CYCLE : MULTI_CYCLE; + stall_jump = ~BranchTargetALU; + jump_set = jump_set_dec; + end + alu_multicycle_dec: begin + stall_alu = 1'b1; + id_fsm_d = MULTI_CYCLE; + rf_we_raw = 1'b0; + end + default: begin + id_fsm_d = FIRST_CYCLE; + end + endcase + end + + MULTI_CYCLE: begin + if(multdiv_en_dec) begin + rf_we_raw = rf_we_dec & ex_valid_i; + end + + if (multicycle_done & ready_wb_i) begin + id_fsm_d = FIRST_CYCLE; + end else begin + stall_multdiv = multdiv_en_dec; + stall_branch = branch_in_dec; + stall_jump = jump_in_dec; + end + end + + // default: begin + // id_fsm_d = FIRST_CYCLE; + // end + endcase + end + end + + // Note for the two-stage configuration ready_wb_i is always set + assign multdiv_ready_id_o = ready_wb_i; + + + // Stall ID/EX stage for reason that relates to instruction in ID/EX + assign stall_id = stall_ld_hz | stall_mem | stall_multdiv | stall_jump | stall_branch | + stall_alu; + + assign instr_done = ~stall_id & ~flush_id & instr_executing; + + // Signal instruction in ID is in it's first cycle. It can remain in its + // first cycle if it is stalled. + assign instr_first_cycle = instr_valid_i & (id_fsm_q == FIRST_CYCLE); + // Used by RVFI to know when to capture register read data + // Used by ALU to access RS3 if ternary instruction. + assign instr_first_cycle_id_o = instr_first_cycle; + + if (WritebackStage) begin : gen_stall_mem + // Register read address matches write address in WB + logic rf_rd_a_wb_match; + logic rf_rd_b_wb_match; + logic fp_rf_rd_a_wb_match; + logic fp_rf_rd_b_wb_match; + logic fp_rf_rd_c_wb_match; + // Hazard between registers being read and written + logic rf_rd_a_hz; + logic rf_rd_b_hz; + logic rf_rd_c_hz; + + logic outstanding_memory_access; + + logic instr_kill; + + assign multicycle_done = lsu_req_dec ? ~stall_mem : ex_valid_i; + + // Is a memory access ongoing that isn't finishing this cycle + assign outstanding_memory_access = (outstanding_load_wb_i | outstanding_store_wb_i) & + ~lsu_resp_valid_i; + + // Can start a new memory access if any previous one has finished or is finishing + assign data_req_allowed = ~outstanding_memory_access; + + // Instruction won't execute because: + // - There is a pending exception in writeback + // The instruction in ID/EX will be flushed and the core will jump to an exception handler + // - The controller isn't running instructions + // This either happens in preparation for a flush and jump to an exception handler e.g. in + // response to an IRQ or debug request or whilst the core is sleeping or resetting/fetching + // first instruction in which case any valid instruction in ID/EX should be ignored. + // - There was an error on instruction fetch + assign instr_kill = instr_fetch_err_i | + wb_exception | + ~controller_run; + + // With writeback stage instructions must be prevented from executing if there is: + // - A load hazard + // - A pending memory access + // If it receives an error response this results in a precise exception from WB so ID/EX + // instruction must not execute until error response is known). + // - A load/store error + // This will cause a precise exception for the instruction in WB so ID/EX instruction must not + // execute + assign instr_executing = instr_valid_i & + ~instr_kill & + ~stall_ld_hz & + ~outstanding_memory_access; + + + // Stall for reasons related to memory: + // * There is an outstanding memory access that won't resolve this cycle (need to wait to allow + // precise exceptions) + // * There is a load/store request not being granted or which is unaligned and waiting to issue + // a second request (needs to stay in ID for the address calculation) + assign stall_mem = instr_valid_i & + (outstanding_memory_access | (lsu_req_dec & ~lsu_req_done_i)); + + // If we stall a load in ID for any reason, it must not make an LSU request + // (otherwide we might issue two requests for the same instruction) + + + assign rf_rd_a_wb_match = (rf_waddr_wb_i == rf_raddr_a_o) & |rf_raddr_a_o; + assign rf_rd_b_wb_match = (rf_waddr_wb_i == rf_raddr_b_o) & |rf_raddr_b_o; + + assign fp_rf_rd_a_wb_match = (rf_waddr_wb_i == rf_raddr_a_o); + assign fp_rf_rd_b_wb_match = (rf_waddr_wb_i == rf_raddr_b_o); + + assign fp_rf_rd_c_wb_match = (rf_waddr_wb_i == fp_rf_raddr_c_o); + + assign rf_rd_a_wb_match_o = rf_rd_a_wb_match; + assign rf_rd_b_wb_match_o = rf_rd_b_wb_match; + + // If instruction is reading register that load will be writing stall in + // ID until load is complete. No need to stall when reading zero register. + assign rf_rd_a_hz = rf_rd_a_wb_match & (rf_ren_a | use_fp_rs1_o); + assign rf_rd_b_hz = rf_rd_b_wb_match & (rf_ren_b | use_fp_rs2_o); + assign rf_rd_c_hz = rf_rd_b_wb_match & use_fp_rs3_o; + + // If instruction is read register that writeback is writing forward writeback data to read + // data. Note this doesn't factor in load data as it arrives too late, such hazards are + // resolved via a stall (see above). + + assign rf_rdata_a_fwd = rf_rd_a_wb_match & rf_write_wb_i ? rf_wdata_fwd_wb_i : rf_rdata_a_i; + assign rf_rdata_b_fwd = rf_rd_b_wb_match & rf_write_wb_i ? rf_wdata_fwd_wb_i : rf_rdata_b_i; + + // forwarding for floating point unit + assign fp_rf_rdata_a_fwd = fp_rf_rd_a_wb_match & fp_rf_write_wb_i ? fp_rf_wdata_fwd_wb_i : fp_rf_rdata_a_i; + assign fp_rf_rdata_b_fwd = fp_rf_rd_b_wb_match & fp_rf_write_wb_i ? fp_rf_wdata_fwd_wb_i : fp_rf_rdata_b_i; + assign fp_rf_rdata_c_fwd = fp_rf_rd_c_wb_match & fp_rf_write_wb_i ? fp_rf_wdata_fwd_wb_i : fp_rf_rdata_c_i; + + assign stall_ld_hz = outstanding_load_wb_i & (rf_rd_a_hz | rf_rd_b_hz | rf_rd_c_hz); + + assign instr_type_wb_o = ~lsu_req_dec ? WB_INSTR_OTHER : + lsu_we ? WB_INSTR_STORE : + WB_INSTR_LOAD; + + assign instr_id_done_o = en_wb_o & ready_wb_i; + + // Stall ID/EX as instruction in ID/EX cannot proceed to writeback yet + assign stall_wb = en_wb_o & ~ready_wb_i; + + assign perf_dside_wait_o = instr_valid_i & ~instr_kill & + (outstanding_memory_access | stall_ld_hz); + end else begin : gen_no_stall_mem + + assign multicycle_done = lsu_req_dec ? lsu_resp_valid_i : ex_valid_i; + + assign data_req_allowed = instr_first_cycle; + + // Without Writeback Stage always stall the first cycle of a load/store. + // Then stall until it is complete + assign stall_mem = instr_valid_i & (lsu_req_dec & (~lsu_resp_valid_i | instr_first_cycle)); + + // No load hazards without Writeback Stage + assign stall_ld_hz = 1'b0; + + // Without writeback stage any valid instruction that hasn't seen an error will execute + assign instr_executing = instr_valid_i & ~instr_fetch_err_i & controller_run; + + + // No data forwarding without writeback stage so always take source register data direct from + // register file + assign rf_rdata_a_fwd = rf_rdata_a_i; + assign rf_rdata_b_fwd = rf_rdata_b_i; + + assign fp_rf_rdata_a_fwd = fp_rf_rdata_a_i; + assign fp_rf_rdata_b_fwd = fp_rf_rdata_b_i; + assign fp_rf_rdata_c_fwd = fp_rf_rdata_c_i; + + assign rf_rd_a_wb_match_o = 1'b0; + assign rf_rd_b_wb_match_o = 1'b0; + + // Unused Writeback stage only IO & wiring + // Assign inputs and internal wiring to unused signals to satisfy lint checks + // Tie-off outputs to constant values + logic unused_data_req_done_ex; + logic [4:0] unused_rf_waddr_wb; + logic unused_rf_write_wb; + logic unused_outstanding_load_wb; + logic unused_outstanding_store_wb; + logic unused_wb_exception; + logic [31:0] unused_rf_wdata_fwd_wb; + + assign unused_data_req_done_ex = lsu_req_done_i; + assign unused_rf_waddr_wb = rf_waddr_wb_i; + assign unused_rf_write_wb = rf_write_wb_i; + assign unused_outstanding_load_wb = outstanding_load_wb_i; + assign unused_outstanding_store_wb = outstanding_store_wb_i; + assign unused_wb_exception = wb_exception; + assign unused_rf_wdata_fwd_wb = rf_wdata_fwd_wb_i; + + assign instr_type_wb_o = WB_INSTR_OTHER; + assign stall_wb = 1'b0; + + assign perf_dside_wait_o = instr_executing & lsu_req_dec & ~lsu_resp_valid_i; + + assign instr_id_done_o = instr_done; + end + + /* Swap operands */ + always_comb begin : swapping + fpu_op_a = use_fp_rs1_o ? fp_rf_rdata_a_fwd : rf_rdata_a_fwd; + fpu_op_b = use_fp_rs2_o ? fp_rf_rdata_b_fwd : rf_rdata_b_fwd; + if (fp_swap_oprnds) begin + fpu_op_c = fpu_op_a; + end else begin + fpu_op_c = fp_rf_rdata_c_fwd; + end + fp_operands_o = {fpu_op_c , fpu_op_b , fpu_op_a}; + end + + assign result_wb = mv_instr ? fpu_op_a : result_ex_i; + + // Signal which instructions to count as retired in minstret, all traps along with ebrk and + // ecall instructions are not counted. + assign instr_perf_count_id_o = ~ebrk_insn & ~ecall_insn_dec & ~illegal_insn_dec & + ~illegal_csr_insn_i & ~instr_fetch_err_i; + + // An instruction is ready to move to the writeback stage (or retire if there is no writeback + // stage) + assign en_wb_o = instr_done; + + assign perf_mul_wait_o = stall_multdiv & mult_en_dec; + assign perf_div_wait_o = stall_multdiv & div_en_dec; + + +endmodule
diff --git a/verilog/rtl/brq_idu_controller.sv b/verilog/rtl/brq_idu_controller.sv new file mode 100644 index 0000000..c35f470 --- /dev/null +++ b/verilog/rtl/brq_idu_controller.sv
@@ -0,0 +1,815 @@ +/** + * Main controller of the processor + */ + +module brq_idu_controller #( + parameter bit WritebackStage = 0, + parameter bit BranchPredictor = 0 + ) ( + input logic clk_i, + input logic rst_ni, + + output logic ctrl_busy_o, // core is busy processing instrs + + // decoder related signals + input logic illegal_insn_i, // decoder has an invalid instr + input logic ecall_insn_i, // decoder has ECALL instr + input logic mret_insn_i, // decoder has MRET instr + input logic dret_insn_i, // decoder has DRET instr + input logic wfi_insn_i, // decoder has WFI instr + input logic ebrk_insn_i, // decoder has EBREAK instr + input logic csr_pipe_flush_i, // do CSR-related pipeline flush + + // instr from IF-ID pipeline stage + input logic instr_valid_i, // instr is valid + input logic [31:0] instr_i, // uncompressed instr data for mtval + input logic [15:0] instr_compressed_i, // instr compressed data for mtval + input logic instr_is_compressed_i, // instr is compressed + input logic instr_bp_taken_i, // instr was predicted taken branch + input logic instr_fetch_err_i, // instr has error + input logic instr_fetch_err_plus2_i, // instr error is x32 + input logic [31:0] pc_id_i, // instr address + + // to IF-ID pipeline stage + output logic instr_valid_clear_o, // kill instr in IF-ID reg + output logic id_in_ready_o, // ID stage is ready for new instr + output logic controller_run_o, // Controller is in standard instruction + // run mode + + // to prefetcher + output logic instr_req_o, // start fetching instructions + output logic pc_set_o, // jump to address set by pc_mux + output logic pc_set_spec_o, // speculative branch + output brq_pkg::pc_sel_e pc_mux_o, // IF stage fetch address selector + // (boot, normal, exception...) + output logic nt_branch_mispredict_o, // Not-taken branch in ID/EX was + // mispredicted (predicted taken) + output brq_pkg::exc_pc_sel_e exc_pc_mux_o, // IF stage selector for exception PC + output brq_pkg::exc_cause_e exc_cause_o, // for IF stage, CSRs + + // LSU + input logic [31:0] lsu_addr_last_i, // for mtval + input logic load_err_i, + input logic store_err_i, + output logic wb_exception_o, // Instruction in WB taking an exception + + // jump/branch signals + input logic branch_set_i, // branch set signal (branch definitely + // taken) + input logic branch_set_spec_i, // speculative branch signal (branch + // may be taken) + input logic branch_not_set_i, // branch is definitely not taken + input logic jump_set_i, // jump taken set signal + + // interrupt signals + input logic csr_mstatus_mie_i, // M-mode interrupt enable bit + input logic irq_pending_i, // interrupt request pending + input brq_pkg::irqs_t irqs_i, // interrupt requests qualified with + // mie CSR + input logic irq_nm_i, // non-maskeable interrupt + output logic nmi_mode_o, // core executing NMI handler + + // debug signals + input logic debug_req_i, + output brq_pkg::dbg_cause_e debug_cause_o, + output logic debug_csr_save_o, + output logic debug_mode_o, + input logic debug_single_step_i, + input logic debug_ebreakm_i, + input logic debug_ebreaku_i, + input logic trigger_match_i, + + output logic csr_save_if_o, + output logic csr_save_id_o, + output logic csr_save_wb_o, + output logic csr_restore_mret_id_o, + output logic csr_restore_dret_id_o, + output logic csr_save_cause_o, + output logic [31:0] csr_mtval_o, + input brq_pkg::priv_lvl_e priv_mode_i, + input logic csr_mstatus_tw_i, + + // stall & flush signals + input logic stall_id_i, + input logic stall_wb_i, + output logic flush_id_o, + input logic ready_wb_i, + + // performance monitors + output logic perf_jump_o, // we are executing a jump + // instruction (j, jr, jal, jalr) + output logic perf_tbranch_o, // we are executing a taken branch + // instruction + input logic fpu_busy_i +); + import brq_pkg::*; + + // FSM state encoding + typedef enum logic [3:0] { + RESET, BOOT_SET, WAIT_SLEEP, SLEEP, FIRST_FETCH, DECODE, FLUSH, + IRQ_TAKEN, DBG_TAKEN_IF, DBG_TAKEN_ID + } ctrl_fsm_e; + + ctrl_fsm_e ctrl_fsm_cs, ctrl_fsm_ns; + + logic nmi_mode_q, nmi_mode_d; + logic debug_mode_q, debug_mode_d; + logic load_err_q, load_err_d; + logic store_err_q, store_err_d; + logic exc_req_q, exc_req_d; + logic illegal_insn_q, illegal_insn_d; + + // Of the various exception/fault signals, which one takes priority in FLUSH and hence controls + // what happens next (setting exc_cause, csr_mtval etc) + logic instr_fetch_err_prio; + logic illegal_insn_prio; + logic ecall_insn_prio; + logic ebrk_insn_prio; + logic store_err_prio; + logic load_err_prio; + + logic stall; + logic halt_if; + logic retain_id; + logic flush_id; + logic illegal_dret; + logic illegal_umode; + logic exc_req_lsu; + logic special_req_all; + logic special_req_branch; + logic enter_debug_mode; + logic ebreak_into_debug; + logic handle_irq; + + logic [3:0] mfip_id; + logic unused_irq_timer; + + logic ecall_insn; + logic mret_insn; + logic dret_insn; + logic wfi_insn; + logic ebrk_insn; + logic csr_pipe_flush; + logic instr_fetch_err; + +`ifndef SYNTHESIS + // synopsys translate_off + // make sure we are called later so that we do not generate messages for + // glitches + always_ff @(negedge clk_i) begin + // print warning in case of decoding errors + if ((ctrl_fsm_cs == DECODE) && instr_valid_i && !instr_fetch_err_i && illegal_insn_d) begin + $display("%t: Illegal instruction (hart %0x) at PC 0x%h: 0x%h", $time, brq_core.hart_id_i, + brq_idu.pc_id_i, brq_idu.instr_rdata_i); + end + end + // synopsys translate_on +`endif + + //////////////// + // Exceptions // + //////////////// + + assign load_err_d = load_err_i; + assign store_err_d = store_err_i; + + // Decoder doesn't take instr_valid into account, factor it in here. + assign ecall_insn = ecall_insn_i & instr_valid_i; + assign mret_insn = mret_insn_i & instr_valid_i; + assign dret_insn = dret_insn_i & instr_valid_i; + assign wfi_insn = wfi_insn_i & instr_valid_i; + assign ebrk_insn = ebrk_insn_i & instr_valid_i; + assign csr_pipe_flush = csr_pipe_flush_i & instr_valid_i; + assign instr_fetch_err = instr_fetch_err_i & instr_valid_i; + + // "Executing DRET outside of Debug Mode causes an illegal instruction exception." + // [Debug Spec v0.13.2, p.41] + assign illegal_dret = dret_insn & ~debug_mode_q; + + // Some instructions can only be executed in M-Mode + assign illegal_umode = (priv_mode_i != PRIV_LVL_M) & + // MRET must be in M-Mode. TW means trap WFI to M-Mode. + (mret_insn | (csr_mstatus_tw_i & wfi_insn)); + + // This is recorded in the illegal_insn_q flop to help timing. Specifically + // it is needed to break the path from brq_cs_registers/illegal_csr_insn_o + // to pc_set_o. Clear when controller is in FLUSH so it won't remain set + // once illegal instruction is handled. + // All terms in this expression are qualified by instr_valid_i + assign illegal_insn_d = (illegal_insn_i | illegal_dret | illegal_umode) & (ctrl_fsm_cs != FLUSH); + + // exception requests + // requests are flopped in exc_req_q. This is cleared when controller is in + // the FLUSH state so the cycle following exc_req_q won't remain set for an + // exception request that has just been handled. + // All terms in this expression are qualified by instr_valid_i + assign exc_req_d = (ecall_insn | ebrk_insn | illegal_insn_d | instr_fetch_err) & + (ctrl_fsm_cs != FLUSH); + + // LSU exception requests + assign exc_req_lsu = store_err_i | load_err_i; + + + // special requests: special instructions, pipeline flushes, exceptions... + + // To avoid creating a path from data_err_i -> instr_req_o and to help timing the below + // special_req_all has a version that only applies to branches. For a branch the controller needs + // to set pc_set_o but only if there is no special request. If the generic special_req_all signal + // is used then a variety of signals that will never cause a special request during a branch + // instruction end up factored into pc_set_o. The special_req_branch only considers the special + // request reasons that are relevant to a branch. + + // generic special request signal, applies to all instructions + // All terms in this expression are qualified by instr_valid_i except exc_req_lsu which can come + // from the Writeback stage with no instr_valid_i from the ID stage + assign special_req_all = mret_insn | dret_insn | wfi_insn | csr_pipe_flush | + exc_req_d | exc_req_lsu; + + // special request that can specifically occur during branch instructions + // All terms in this expression are qualified by instr_valid_i + assign special_req_branch = instr_fetch_err & (ctrl_fsm_cs != FLUSH); + + + // Exception/fault prioritisation is taken from Table 3.7 of Priviledged Spec v1.11 + if (WritebackStage) begin : g_wb_exceptions + always_comb begin + instr_fetch_err_prio = 0; + illegal_insn_prio = 0; + ecall_insn_prio = 0; + ebrk_insn_prio = 0; + store_err_prio = 0; + load_err_prio = 0; + + // Note that with the writeback stage store/load errors occur on the instruction in writeback, + // all other exception/faults occur on the instruction in ID/EX. The faults from writeback + // must take priority as that instruction is architecurally ordered before the one in ID/EX. + if (store_err_q) begin + store_err_prio = 1'b1; + end else if (load_err_q) begin + load_err_prio = 1'b1; + end else if (instr_fetch_err) begin + instr_fetch_err_prio = 1'b1; + end else if (illegal_insn_q) begin + illegal_insn_prio = 1'b1; + end else if (ecall_insn) begin + ecall_insn_prio = 1'b1; + end else if (ebrk_insn) begin + ebrk_insn_prio = 1'b1; + end + end + + // Instruction in writeback is generating an exception so instruction in ID must not execute + assign wb_exception_o = load_err_q | store_err_q | load_err_i | store_err_i; + end else begin : g_no_wb_exceptions + always_comb begin + instr_fetch_err_prio = 0; + illegal_insn_prio = 0; + ecall_insn_prio = 0; + ebrk_insn_prio = 0; + store_err_prio = 0; + load_err_prio = 0; + + if (instr_fetch_err) begin + instr_fetch_err_prio = 1'b1; + end else if (illegal_insn_q) begin + illegal_insn_prio = 1'b1; + end else if (ecall_insn) begin + ecall_insn_prio = 1'b1; + end else if (ebrk_insn) begin + ebrk_insn_prio = 1'b1; + end else if (store_err_q) begin + store_err_prio = 1'b1; + end else if (load_err_q) begin + load_err_prio = 1'b1; + end + end + assign wb_exception_o = 1'b0; + end + + + //////////////// + // Interrupts // + //////////////// + + // Enter debug mode due to an external debug_req_i or because the core is in + // single step mode (dcsr.step == 1). Single step must be qualified with + // instruction valid otherwise the core will immediately enter debug mode + // due to a recently flushed IF (or a delay in an instruction returning from + // memory) before it has had anything to single step. + // Also enter debug mode on a trigger match (hardware breakpoint) + assign enter_debug_mode = (debug_req_i | (debug_single_step_i & instr_valid_i) | + trigger_match_i) & ~debug_mode_q; + + // Set when an ebreak should enter debug mode rather than jump to exception + // handler + assign ebreak_into_debug = priv_mode_i == PRIV_LVL_M ? debug_ebreakm_i : + priv_mode_i == PRIV_LVL_U ? debug_ebreaku_i : + 1'b0; + + // Interrupts including NMI are ignored, + // - while in debug mode [Debug Spec v0.13.2, p.39], + // - while in NMI mode (nested NMIs are not supported, NMI has highest priority and + // cannot be interrupted by regular interrupts). + assign handle_irq = ~debug_mode_q & ~nmi_mode_q & + (irq_nm_i | (irq_pending_i & csr_mstatus_mie_i)); + + // generate ID of fast interrupts, highest priority to highest ID + always_comb begin : gen_mfip_id + if (irqs_i.irq_fast[14]) mfip_id = 4'd14; + else if (irqs_i.irq_fast[13]) mfip_id = 4'd13; + else if (irqs_i.irq_fast[12]) mfip_id = 4'd12; + else if (irqs_i.irq_fast[11]) mfip_id = 4'd11; + else if (irqs_i.irq_fast[10]) mfip_id = 4'd10; + else if (irqs_i.irq_fast[ 9]) mfip_id = 4'd9; + else if (irqs_i.irq_fast[ 8]) mfip_id = 4'd8; + else if (irqs_i.irq_fast[ 7]) mfip_id = 4'd7; + else if (irqs_i.irq_fast[ 6]) mfip_id = 4'd6; + else if (irqs_i.irq_fast[ 5]) mfip_id = 4'd5; + else if (irqs_i.irq_fast[ 4]) mfip_id = 4'd4; + else if (irqs_i.irq_fast[ 3]) mfip_id = 4'd3; + else if (irqs_i.irq_fast[ 2]) mfip_id = 4'd2; + else if (irqs_i.irq_fast[ 1]) mfip_id = 4'd1; + else mfip_id = 4'd0; + end + + assign unused_irq_timer = irqs_i.irq_timer; + + ///////////////////// + // Core controller // + ///////////////////// + + always_comb begin + // Default values + instr_req_o = 1'b1; + + csr_save_if_o = 1'b0; + csr_save_id_o = 1'b0; + csr_save_wb_o = 1'b0; + csr_restore_mret_id_o = 1'b0; + csr_restore_dret_id_o = 1'b0; + csr_save_cause_o = 1'b0; + csr_mtval_o = '0; + + // The values of pc_mux and exc_pc_mux are only relevant if pc_set is set. Some of the states + // below always set pc_mux and exc_pc_mux but only set pc_set if certain conditions are met. + // This avoid having to factor those conditions into the pc_mux and exc_pc_mux select signals + // helping timing. + pc_mux_o = PC_BOOT; + pc_set_o = 1'b0; + pc_set_spec_o = 1'b0; + nt_branch_mispredict_o = 1'b0; + + exc_pc_mux_o = EXC_PC_IRQ; + exc_cause_o = EXC_CAUSE_INSN_ADDR_MISA; // = 6'h00 + + ctrl_fsm_ns = ctrl_fsm_cs; + + ctrl_busy_o = 1'b1; + + halt_if = 1'b0; + retain_id = 1'b0; + flush_id = 1'b0; + + debug_csr_save_o = 1'b0; + debug_cause_o = DBG_CAUSE_EBREAK; + debug_mode_d = debug_mode_q; + nmi_mode_d = nmi_mode_q; + + perf_tbranch_o = 1'b0; + perf_jump_o = 1'b0; + + controller_run_o = 1'b0; + + unique case (ctrl_fsm_cs) + RESET: begin + instr_req_o = 1'b0; + pc_mux_o = PC_BOOT; + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + ctrl_fsm_ns = BOOT_SET; + end + + BOOT_SET: begin + // copy boot address to instr fetch address + instr_req_o = 1'b1; + pc_mux_o = PC_BOOT; + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + + ctrl_fsm_ns = FIRST_FETCH; + end + + WAIT_SLEEP: begin + ctrl_busy_o = 1'b0; + instr_req_o = 1'b0; + halt_if = 1'b1; + flush_id = 1'b1; + ctrl_fsm_ns = SLEEP; + end + + SLEEP: begin + // instruction in IF stage is already valid + // we begin execution when an interrupt has arrived + instr_req_o = 1'b0; + halt_if = 1'b1; + flush_id = 1'b1; + + // normal execution flow + // in debug mode or single step mode we leave immediately (wfi=nop) + if (irq_nm_i || irq_pending_i || debug_req_i || debug_mode_q || debug_single_step_i) begin + ctrl_fsm_ns = FIRST_FETCH; + end else begin + // Make sure clock remains disabled. + ctrl_busy_o = 1'b0; + end + end + + FIRST_FETCH: begin + // Stall because of IF miss + if (id_in_ready_o) begin + ctrl_fsm_ns = DECODE; + end + + // handle interrupts + if (handle_irq) begin + // We are handling an interrupt. Set halt_if to tell IF not to give + // us any more instructions before it redirects to the handler, but + // don't set flush_id: we must allow this instruction to complete + // (since it might have outstanding loads or stores). + ctrl_fsm_ns = IRQ_TAKEN; + halt_if = 1'b1; + end + + // enter debug mode + if (enter_debug_mode) begin + ctrl_fsm_ns = DBG_TAKEN_IF; + // Halt IF only for now, ID will be flushed in DBG_TAKEN_IF as the + // ID state is needed for correct debug mode entry + halt_if = 1'b1; + end + end + + DECODE: begin + // normal operating mode of the ID stage, in case of debug and interrupt requests, + // priorities are as follows (lower number == higher priority) + // 1. currently running (multicycle) instructions and exceptions caused by these + // 2. debug requests + // 3. interrupt requests + + controller_run_o = 1'b1; + + // Set PC mux for branch and jump here to ease timing. Value is only relevant if pc_set_o is + // also set. Setting the mux value here avoids factoring in special_req and instr_valid_i + // which helps timing. + pc_mux_o = PC_JUMP; + + + // Get ready for special instructions, exceptions, pipeline flushes + if (special_req_all) begin + // Halt IF but don't flush ID. This leaves a valid instruction in + // ID so controller can determine appropriate action in the + // FLUSH state. + retain_id = 1'b1; + + // Wait for the writeback stage to either be ready for a new instruction or raise its own + // exception before going to FLUSH. If the instruction in writeback raises an exception it + // must take priority over any exception from an instruction in ID/EX. Only once the + // writeback stage is ready can we be certain that won't happen. Without a writeback + // stage ready_wb_i == 1 so the FSM will always go directly to FLUSH. + + if (ready_wb_i | wb_exception_o) begin + ctrl_fsm_ns = FLUSH; + end + end + + if (!special_req_branch) begin + if (branch_set_i || jump_set_i) begin + // Only set the PC if the branch predictor hasn't already done the branch for us + pc_set_o = BranchPredictor ? ~instr_bp_taken_i : 1'b1; + + perf_tbranch_o = branch_set_i; + perf_jump_o = jump_set_i; + end + + if (BranchPredictor) begin + if (instr_bp_taken_i & branch_not_set_i) begin + // If the instruction is a branch that was predicted to be taken but was not taken + // signal a mispredict. + nt_branch_mispredict_o = 1'b1; + end + end + end + + // pc_set signal excluding branch taken condition + if ((branch_set_spec_i || jump_set_i) && !special_req_branch) begin + // Only speculatively set the PC if the branch predictor hasn't already done the branch + // for us + pc_set_spec_o = BranchPredictor ? ~instr_bp_taken_i : 1'b1; + end + + // If entering debug mode or handling an IRQ the core needs to wait + // until the current instruction has finished executing. Stall IF + // during that time. + if ((enter_debug_mode || handle_irq) && stall) begin + halt_if = 1'b1; + end + + if (!stall && !special_req_all) begin + if (enter_debug_mode) begin + // enter debug mode + ctrl_fsm_ns = DBG_TAKEN_IF; + // Halt IF only for now, ID will be flushed in DBG_TAKEN_IF as the + // ID state is needed for correct debug mode entry + halt_if = 1'b1; + end else if (handle_irq) begin + // handle interrupt (not in debug mode) + ctrl_fsm_ns = IRQ_TAKEN; + // We are handling an interrupt (not in debug mode). Set halt_if to + // tell IF not to give us any more instructions before it redirects + // to the handler, but don't set flush_id: we must allow this + // instruction to complete (since it might have outstanding loads + // or stores). + halt_if = 1'b1; + end + end + + end // DECODE + + IRQ_TAKEN: begin + pc_mux_o = PC_EXC; + exc_pc_mux_o = EXC_PC_IRQ; + + if (handle_irq) begin + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + + csr_save_if_o = 1'b1; + csr_save_cause_o = 1'b1; + + // interrupt priorities according to Privileged Spec v1.11 p.31 + if (irq_nm_i && !nmi_mode_q) begin + exc_cause_o = EXC_CAUSE_IRQ_NM; + nmi_mode_d = 1'b1; // enter NMI mode + end else if (irqs_i.irq_fast != 15'b0) begin + // generate exception cause ID from fast interrupt ID: + // - first bit distinguishes interrupts from exceptions, + // - second bit adds 16 to fast interrupt ID + // for example EXC_CAUSE_IRQ_FAST_0 = {1'b1, 5'd16} + exc_cause_o = exc_cause_e'({2'b11, mfip_id}); + end else if (irqs_i.irq_external) begin + exc_cause_o = EXC_CAUSE_IRQ_EXTERNAL_M; + end else if (irqs_i.irq_software) begin + exc_cause_o = EXC_CAUSE_IRQ_SOFTWARE_M; + end else begin // irqs_i.irq_timer + exc_cause_o = EXC_CAUSE_IRQ_TIMER_M; + end + end + + ctrl_fsm_ns = DECODE; + end + + DBG_TAKEN_IF: begin + pc_mux_o = PC_EXC; + exc_pc_mux_o = EXC_PC_DBD; + + // enter debug mode and save PC in IF to dpc + // jump to debug exception handler in debug memory + if (debug_single_step_i || debug_req_i || trigger_match_i) begin + flush_id = 1'b1; + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + + csr_save_if_o = 1'b1; + debug_csr_save_o = 1'b1; + + csr_save_cause_o = 1'b1; + if (trigger_match_i) begin + debug_cause_o = DBG_CAUSE_TRIGGER; + end else if (debug_single_step_i) begin + debug_cause_o = DBG_CAUSE_STEP; + end else begin + debug_cause_o = DBG_CAUSE_HALTREQ; + end + + // enter debug mode + debug_mode_d = 1'b1; + end + + ctrl_fsm_ns = DECODE; + end + + DBG_TAKEN_ID: begin + // enter debug mode and save PC in ID to dpc, used when encountering + // 1. EBREAK during debug mode + // 2. EBREAK with forced entry into debug mode (ebreakm or ebreaku set). + // regular ebreak's go through FLUSH. + // + // for 1. do not update dcsr and dpc, for 2. do so [Debug Spec v0.13.2, p.39] + // jump to debug exception handler in debug memory + flush_id = 1'b1; + pc_mux_o = PC_EXC; + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + exc_pc_mux_o = EXC_PC_DBD; + + // update dcsr and dpc + if (ebreak_into_debug && !debug_mode_q) begin // ebreak with forced entry + + // dpc (set to the address of the EBREAK, i.e. set to PC in ID stage) + csr_save_cause_o = 1'b1; + csr_save_id_o = 1'b1; + + // dcsr + debug_csr_save_o = 1'b1; + debug_cause_o = DBG_CAUSE_EBREAK; + end + + // enter debug mode + debug_mode_d = 1'b1; + + ctrl_fsm_ns = DECODE; + end + + FLUSH: begin + // flush the pipeline + halt_if = 1'b1; + flush_id = 1'b1; + ctrl_fsm_ns = DECODE; + + // As pc_mux and exc_pc_mux can take various values in this state they aren't set early + // here. + + // exceptions: set exception PC, save PC and exception cause + // exc_req_lsu is high for one clock cycle only (in DECODE) + if (exc_req_q || store_err_q || load_err_q) begin + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + pc_mux_o = PC_EXC; + exc_pc_mux_o = debug_mode_q ? EXC_PC_DBG_EXC : EXC_PC_EXC; + + if (WritebackStage) begin : g_writeback_mepc_save + // With the writeback stage present whether an instruction accessing memory will cause + // an exception is only known when it is in writeback. So when taking such an exception + // epc must come from writeback. + csr_save_id_o = ~(store_err_q | load_err_q); + csr_save_wb_o = store_err_q | load_err_q; + end else begin : g_no_writeback_mepc_save + csr_save_id_o = 1'b0; + end + + csr_save_cause_o = 1'b1; + + // Exception/fault prioritisation logic will have set exactly 1 X_prio signal + unique case (1'b1) + instr_fetch_err_prio: begin + exc_cause_o = EXC_CAUSE_INSTR_ACCESS_FAULT; + csr_mtval_o = instr_fetch_err_plus2_i ? (pc_id_i + 32'd2) : pc_id_i; + end + illegal_insn_prio: begin + exc_cause_o = EXC_CAUSE_ILLEGAL_INSN; + csr_mtval_o = instr_is_compressed_i ? {16'b0, instr_compressed_i} : instr_i; + end + ecall_insn_prio: begin + exc_cause_o = (priv_mode_i == PRIV_LVL_M) ? EXC_CAUSE_ECALL_MMODE : + EXC_CAUSE_ECALL_UMODE; + end + ebrk_insn_prio: begin + if (debug_mode_q | ebreak_into_debug) begin + /* + * EBREAK in debug mode re-enters debug mode + * + * "The only exception is EBREAK. When that is executed in Debug + * Mode, it halts the hart again but without updating dpc or + * dcsr." [Debug Spec v0.13.2, p.39] + */ + + /* + * dcsr.ebreakm == 1: + * "EBREAK instructions in M-mode enter Debug Mode." + * [Debug Spec v0.13.2, p.42] + */ + pc_set_o = 1'b0; + pc_set_spec_o = 1'b0; + csr_save_id_o = 1'b0; + csr_save_cause_o = 1'b0; + ctrl_fsm_ns = DBG_TAKEN_ID; + flush_id = 1'b0; + end else begin + /* + * "The EBREAK instruction is used by debuggers to cause control + * to be transferred back to a debugging environment. It + * generates a breakpoint exception and performs no other + * operation. [...] ECALL and EBREAK cause the receiving + * privilege mode's epc register to be set to the address of the + * ECALL or EBREAK instruction itself, not the address of the + * following instruction." [Privileged Spec v1.11, p.40] + */ + exc_cause_o = EXC_CAUSE_BREAKPOINT; + end + end + store_err_prio: begin + exc_cause_o = EXC_CAUSE_STORE_ACCESS_FAULT; + csr_mtval_o = lsu_addr_last_i; + end + load_err_prio: begin + exc_cause_o = EXC_CAUSE_LOAD_ACCESS_FAULT; + csr_mtval_o = lsu_addr_last_i; + end + default: ; + endcase + end else begin + // special instructions and pipeline flushes + if (mret_insn) begin + pc_mux_o = PC_ERET; + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + csr_restore_mret_id_o = 1'b1; + if (nmi_mode_q) begin + nmi_mode_d = 1'b0; // exit NMI mode + end + end else if (dret_insn) begin + pc_mux_o = PC_DRET; + pc_set_o = 1'b1; + pc_set_spec_o = 1'b1; + debug_mode_d = 1'b0; + csr_restore_dret_id_o = 1'b1; + end else if (wfi_insn) begin + ctrl_fsm_ns = WAIT_SLEEP; + end else if (csr_pipe_flush && handle_irq) begin + // start handling IRQs when doing CSR-related pipeline flushes + ctrl_fsm_ns = IRQ_TAKEN; + end + end // exc_req_q + + // Entering debug mode due to either single step or debug_req. Ensure + // registers are set for exception but then enter debug handler rather + // than exception handler [Debug Spec v0.13.2, p.44] + // Leave all other signals as is to ensure CSRs and PC get set as if + // core was entering exception handler, entry to debug mode will then + // see the appropriate state and setup dpc correctly. + // If an EBREAK instruction is causing us to enter debug mode on the + // same cycle as a debug_req or single step, honor the EBREAK and + // proceed to DBG_TAKEN_ID. + if (enter_debug_mode && !(ebrk_insn_prio && ebreak_into_debug)) begin + ctrl_fsm_ns = DBG_TAKEN_IF; + end + end // FLUSH + + default: begin + instr_req_o = 1'b0; + ctrl_fsm_ns = RESET; + end + endcase + end + + assign flush_id_o = flush_id; + + // signal to CSR when in debug mode + assign debug_mode_o = debug_mode_q; + + // signal to CSR when in an NMI handler (for nested exception handling) + assign nmi_mode_o = nmi_mode_q; + + /////////////////// + // Stall control // + /////////////////// + + // If high current instruction cannot complete this cycle. Either because it needs more cycles to + // finish (stall_id_i) or because the writeback stage cannot accept it yet (stall_wb_i). If there + // is no writeback stage stall_wb_i is a constant 0. + assign stall = stall_id_i | stall_wb_i | fpu_busy_i; + + // signal to IF stage that ID stage is ready for next instr + assign id_in_ready_o = ~stall & ~halt_if & ~retain_id; + + // kill instr in IF-ID pipeline reg that are done, or if a + // multicycle instr causes an exception for example + // retain_id is another kind of stall, where the instr_valid bit must remain + // set (unless flush_id is set also). It cannot be factored directly into + // stall as this causes a combinational loop. + assign instr_valid_clear_o = ~(stall | retain_id) | flush_id; + + // update registers + always_ff @(posedge clk_i or negedge rst_ni) begin : update_regs + if (!rst_ni) begin + ctrl_fsm_cs <= RESET; + nmi_mode_q <= 1'b0; + debug_mode_q <= 1'b0; + load_err_q <= 1'b0; + store_err_q <= 1'b0; + exc_req_q <= 1'b0; + illegal_insn_q <= 1'b0; + end else begin + ctrl_fsm_cs <= ctrl_fsm_ns; + nmi_mode_q <= nmi_mode_d; + debug_mode_q <= debug_mode_d; + load_err_q <= load_err_d; + store_err_q <= store_err_d; + exc_req_q <= exc_req_d; + illegal_insn_q <= illegal_insn_d; + end + end + + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_idu_decoder.sv b/verilog/rtl/brq_idu_decoder.sv new file mode 100644 index 0000000..3b4c452 --- /dev/null +++ b/verilog/rtl/brq_idu_decoder.sv
@@ -0,0 +1,1720 @@ +// 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 + +/** + * Instruction decoder + * + * This module is fully combinatorial, clock and reset are used for + * assertions only. + */ + +module brq_idu_decoder #( + parameter bit RV32E = 0, + parameter brq_pkg::rv32m_e RV32M = brq_pkg::RV32MFast, + parameter brq_pkg::rv32b_e RV32B = brq_pkg::RV32BNone, + parameter brq_pkg::rvfloat_e RVF = brq_pkg::RV64FDouble, + parameter bit BranchTargetALU = 0 +) ( + input logic clk_i, + input logic rst_ni, + + // to/from controller + output logic illegal_insn_o, // illegal instr encountered + output logic ebrk_insn_o, // trap instr encountered + output logic mret_insn_o, // return from exception instr + // encountered + output logic dret_insn_o, // return from debug instr encountered + output logic ecall_insn_o, // syscall instr encountered + output logic wfi_insn_o, // wait for interrupt instr encountered + output logic jump_set_o, // jump taken set signal + input logic branch_taken_i, // registered branch decision + output logic icache_inval_o, + + // from IF-ID pipeline register + input logic instr_first_cycle_i, // instruction read is in its first cycle + input logic [31:0] instr_rdata_i, // instruction read from memory/cache + input logic [31:0] instr_rdata_alu_i, // instruction read from memory/cache + // replicated to ease fan-out) + + input logic illegal_c_insn_i, // compressed instruction decode failed + + // immediates + output brq_pkg::imm_a_sel_e imm_a_mux_sel_o, // immediate selection for operand a + output brq_pkg::imm_b_sel_e imm_b_mux_sel_o, // immediate selection for operand b + output brq_pkg::op_a_sel_e bt_a_mux_sel_o, // branch target selection operand a + output brq_pkg::imm_b_sel_e bt_b_mux_sel_o, // branch target selection operand b + output logic [31:0] imm_i_type_o, + output logic [31:0] imm_s_type_o, + output logic [31:0] imm_b_type_o, + output logic [31:0] imm_u_type_o, + output logic [31:0] imm_j_type_o, + output logic [31:0] zimm_rs1_type_o, + + // register file + output brq_pkg::rf_wd_sel_e rf_wdata_sel_o, // RF write data selection + output logic rf_we_o, // write enable for regfile + output logic [4:0] rf_raddr_a_o, + output logic [4:0] rf_raddr_b_o, + output logic [4:0] rf_waddr_o, + output logic rf_ren_a_o, // Instruction reads from RF addr A + output logic rf_ren_b_o, // Instruction reads from RF addr B + + // ALU + output brq_pkg::alu_op_e alu_operator_o, // ALU operation selection + output brq_pkg::op_a_sel_e alu_op_a_mux_sel_o, // operand a selection: reg value, PC, + // immediate or zero + output brq_pkg::op_b_sel_e alu_op_b_mux_sel_o, // operand b selection: reg value or + // immediate + output logic alu_multicycle_o, // ternary bitmanip instruction + + // MULT & DIV + output logic mult_en_o, // perform integer multiplication + output logic div_en_o, // perform integer division or remainder + output logic mult_sel_o, // as above but static, for data muxes + output logic div_sel_o, // as above but static, for data muxes + + output brq_pkg::md_op_e multdiv_operator_o, + output logic [1:0] multdiv_signed_mode_o, + + // CSRs + output logic csr_access_o, // access to CSR + output brq_pkg::csr_op_e csr_op_o, // operation to perform on CSR + + // LSU + output logic data_req_o, // start transaction to data memory + output logic data_we_o, // write enable + output logic [1:0] data_type_o, // size of transaction: byte, half + // word or word + output logic data_sign_extension_o, // sign extension for data read from + // memory + + // jump/branches + output logic jump_in_dec_o, // jump is being calculated in ALU + output logic branch_in_dec_o, + + // Floating point extensions IO + output fpnew_pkg::roundmode_e fp_rounding_mode_o, // defines the rounding mode + output brq_pkg::op_b_sel_e fp_alu_op_b_mux_sel_o, // operand b selection: reg value or + // immediate + output logic [4:0] fp_rf_raddr_a_o, + output logic [4:0] fp_rf_raddr_b_o, + output logic [4:0] fp_rf_raddr_c_o, + + output logic [4:0] fp_rf_waddr_o, + output logic fp_rf_we_o, + + output fpnew_pkg::operation_e fp_alu_operator_o, + output logic fp_alu_op_mod_o, + output logic fp_rm_dynamic_o, + output fpnew_pkg::fp_format_e fp_src_fmt_o, + output fpnew_pkg::fp_format_e fp_dst_fmt_o, + output logic is_fp_instr_o, + output logic use_fp_rs1_o, + output logic use_fp_rs2_o, + output logic use_fp_rs3_o, + output logic use_fp_rd_o, + output logic fp_swap_oprnds_o, + output logic fp_load_o, + output logic mv_instr_o +); + + import brq_pkg::*; + import fpnew_pkg::*; + + logic fp_invalid_rm; + + logic illegal_insn; + logic illegal_reg_rv32e; + logic csr_illegal; + logic rf_we; + + logic [31:0] instr; + logic [31:0] instr_alu; + // Source/Destination register instruction index + logic [4:0] instr_rs1; + logic [4:0] instr_rs2; + logic [4:0] instr_rs3; + logic [4:0] instr_rd; + + logic use_rs3_d; + logic use_rs3_q; + + csr_op_e csr_op; + + opcode_e opcode; + opcode_e opcode_alu; + + // To help timing the flops containing the current instruction are replicated to reduce fan-out. + // instr_alu is used to determine the ALU control logic and associated operand/imm select signals + // as the ALU is often on the more critical timing paths. instr is used for everything else. + assign instr = instr_rdata_i; + assign instr_alu = instr_rdata_alu_i; + + ////////////////////////////////////// + // Register and immediate selection // + ////////////////////////////////////// + + // immediate extraction and sign extension + assign imm_i_type_o = { {20{instr[31]}}, instr[31:20] }; + assign imm_s_type_o = { {20{instr[31]}}, instr[31:25], instr[11:7] }; + assign imm_b_type_o = { {19{instr[31]}}, instr[31], instr[7], instr[30:25], instr[11:8], 1'b0 }; + assign imm_u_type_o = { instr[31:12], 12'b0 }; + assign imm_j_type_o = { {12{instr[31]}}, instr[19:12], instr[20], instr[30:21], 1'b0 }; + + // immediate for CSR manipulation (zero extended) + assign zimm_rs1_type_o = { 27'b0, instr_rs1 }; // rs1 + + // the use of rs3 is known one cycle ahead. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + use_rs3_q <= 1'b0; + end else begin + use_rs3_q <= use_rs3_d; + end + end + + // source registers + assign instr_rs1 = instr[19:15]; + assign instr_rs2 = instr[24:20]; + assign instr_rs3 = instr[31:27]; + assign rf_raddr_a_o = (use_rs3_q & ~instr_first_cycle_i) ? instr_rs3 : instr_rs1; // rs3 / rs1 + assign rf_raddr_b_o = instr_rs2; // rs2 + + // destination register + assign instr_rd = instr[11:7]; + assign rf_waddr_o = instr_rd; // rd + + // fp source registers + assign fp_rf_raddr_a_o = instr_rs1; + assign fp_rf_raddr_b_o = instr_rs2; + assign fp_rf_raddr_c_o = instr_rs3; + + // fp destination register + assign fp_rf_waddr_o = instr_rd; + + assign fp_rounding_mode_o = roundmode_e'(instr[14:12]); + assign fp_invalid_rm = (instr[14:12] == 3'b101) ? 1'b1 : + (instr[14:12] == 3'b110) ? 1'b1 : 1'b0; + assign fp_rm_dynamic_o = (instr[14:12] == 3'b111) ? 1'b1 : 1'b0; + + assign fp_dst_fmt_o = FP32; + + //////////////////// + // Register check // + //////////////////// + if (RV32E) begin : gen_rv32e_reg_check_active + assign illegal_reg_rv32e = ((rf_raddr_a_o[4] & (alu_op_a_mux_sel_o == OP_A_REG_A)) | + (rf_raddr_b_o[4] & (alu_op_b_mux_sel_o == OP_B_REG_B)) | + (rf_waddr_o[4] & rf_we)); + end else begin : gen_rv32e_reg_check_inactive + assign illegal_reg_rv32e = 1'b0; + end + + /////////////////////// + // CSR operand check // + /////////////////////// + always_comb begin : csr_operand_check + csr_op_o = csr_op; + + // CSRRSI/CSRRCI must not write 0 to CSRs (uimm[4:0]=='0) + // CSRRS/CSRRC must not write from x0 to CSRs (rs1=='0) + if ((csr_op == CSR_OP_SET || csr_op == CSR_OP_CLEAR) && + instr_rs1 == '0) begin + csr_op_o = CSR_OP_READ; + end + end + + ///////////// + // Decoder // + ///////////// + + always_comb begin + jump_in_dec_o = 1'b0; + jump_set_o = 1'b0; + branch_in_dec_o = 1'b0; + icache_inval_o = 1'b0; + + multdiv_operator_o = MD_OP_MULL; + multdiv_signed_mode_o = 2'b00; + + rf_wdata_sel_o = RF_WD_EX; + rf_we = 1'b0; + rf_ren_a_o = 1'b0; + rf_ren_b_o = 1'b0; + + csr_access_o = 1'b0; + csr_illegal = 1'b0; + csr_op = CSR_OP_READ; + + data_we_o = 1'b0; + data_type_o = 2'b00; + data_sign_extension_o = 1'b0; + data_req_o = 1'b0; + + illegal_insn = 1'b0; + ebrk_insn_o = 1'b0; + mret_insn_o = 1'b0; + dret_insn_o = 1'b0; + ecall_insn_o = 1'b0; + wfi_insn_o = 1'b0; + + // Floating Point + fp_rf_we_o = 1'b0; + is_fp_instr_o = 1'b0; + use_fp_rs1_o = 1'b0; + use_fp_rs2_o = 1'b0; + use_fp_rs3_o = 1'b0; + use_fp_rd_o = 1'b0; + fp_load_o = 1'b0; + fp_src_fmt_o = FP32; + fp_dst_fmt_o = FP32; + fp_swap_oprnds_o = 1'b0; + mv_instr_o = 1'b0; + + opcode = opcode_e'(instr[6:0]); + + unique case (opcode) + + /////////// + // Jumps // + /////////// + + OPCODE_JAL: begin // Jump and Link + jump_in_dec_o = 1'b1; + + if (instr_first_cycle_i) begin + // Calculate jump target (and store PC + 4 if BranchTargetALU is configured) + rf_we = BranchTargetALU; + jump_set_o = 1'b1; + end else begin + // Calculate and store PC+4 + rf_we = 1'b1; + end + end + + OPCODE_JALR: begin // Jump and Link Register + jump_in_dec_o = 1'b1; + + if (instr_first_cycle_i) begin + // Calculate jump target (and store PC + 4 if BranchTargetALU is configured) + rf_we = BranchTargetALU; + jump_set_o = 1'b1; + end else begin + // Calculate and store PC+4 + rf_we = 1'b1; + end + if (instr[14:12] != 3'b0) begin + illegal_insn = 1'b1; + end + + rf_ren_a_o = 1'b1; + end + + OPCODE_BRANCH: begin // Branch + branch_in_dec_o = 1'b1; + // Check branch condition selection + unique case (instr[14:12]) + 3'b000, + 3'b001, + 3'b100, + 3'b101, + 3'b110, + 3'b111: illegal_insn = 1'b0; + default: illegal_insn = 1'b1; + endcase + rf_ren_a_o = 1'b1; + rf_ren_b_o = 1'b1; + end + + //////////////// + // Load/store // + //////////////// + + OPCODE_STORE: begin + rf_ren_a_o = 1'b1; + rf_ren_b_o = 1'b1; + data_req_o = 1'b1; + data_we_o = 1'b1; + + if (instr[14]) begin + illegal_insn = 1'b1; + end + + // store size + unique case (instr[13:12]) + 2'b00: data_type_o = 2'b10; // sb + 2'b01: data_type_o = 2'b01; // sh + 2'b10: data_type_o = 2'b00; // sw + default: illegal_insn = 1'b1; + endcase + end + + OPCODE_LOAD: begin + rf_ren_a_o = 1'b1; + data_req_o = 1'b1; + data_type_o = 2'b00; + + // sign/zero extension + data_sign_extension_o = ~instr[14]; + + // load size + unique case (instr[13:12]) + 2'b00: data_type_o = 2'b10; // lb(u) + 2'b01: data_type_o = 2'b01; // lh(u) + 2'b10: begin + data_type_o = 2'b00; // lw + if (instr[14]) begin + illegal_insn = 1'b1; // lwu does not exist + end + end + default: begin + illegal_insn = 1'b1; + end + endcase + end + + ///////// + // ALU // + ///////// + + OPCODE_LUI: begin // Load Upper Immediate + rf_we = 1'b1; + end + + OPCODE_AUIPC: begin // Add Upper Immediate to PC + rf_we = 1'b1; + end + + OPCODE_OP_IMM: begin // Register-Immediate ALU Operations + rf_ren_a_o = 1'b1; + rf_we = 1'b1; + + unique case (instr[14:12]) + 3'b000, + 3'b010, + 3'b011, + 3'b100, + 3'b110, + 3'b111: illegal_insn = 1'b0; + + 3'b001: begin + unique case (instr[31:27]) + 5'b0_0000: illegal_insn = (instr[26:25] == 2'b00) ? 1'b0 : 1'b1; // slli + 5'b0_0100, // sloi + 5'b0_1001, // sbclri + 5'b0_0101, // sbseti + 5'b0_1101: illegal_insn = (RV32B != RV32BNone) ? 1'b0 : 1'b1; // sbinvi + 5'b0_0001: if (instr[26] == 1'b0) begin + illegal_insn = (RV32B == RV32BFull) ? 1'b0 : 1'b1; // shfl + end else begin + illegal_insn = 1'b1; + end + 5'b0_1100: begin + unique case(instr[26:20]) + 7'b000_0000, // clz + 7'b000_0001, // ctz + 7'b000_0010, // pcnt + 7'b000_0100, // sext.b + 7'b000_0101: illegal_insn = (RV32B != RV32BNone) ? 1'b0 : 1'b1; // sext.h + 7'b001_0000, // crc32.b + 7'b001_0001, // crc32.h + 7'b001_0010, // crc32.w + 7'b001_1000, // crc32c.b + 7'b001_1001, // crc32c.h + 7'b001_1010: illegal_insn = (RV32B == RV32BFull) ? 1'b0 : 1'b1; // crc32c.w + + default: illegal_insn = 1'b1; + endcase + end + default : illegal_insn = 1'b1; + endcase + end + + 3'b101: begin + if (instr[26]) begin + illegal_insn = (RV32B != RV32BNone) ? 1'b0 : 1'b1; // fsri + end else begin + unique case (instr[31:27]) + 5'b0_0000, // srli + 5'b0_1000: illegal_insn = (instr[26:25] == 2'b00) ? 1'b0 : 1'b1; // srai + + 5'b0_0100, // sroi + 5'b0_1100, // rori + 5'b0_1001: illegal_insn = (RV32B != RV32BNone) ? 1'b0 : 1'b1; // sbexti + + 5'b0_1101: begin + if ((RV32B == RV32BFull)) begin + illegal_insn = 1'b0; // grevi + end else begin + unique case (instr[24:20]) + 5'b11111, // rev + 5'b11000: illegal_insn = (RV32B == RV32BBalanced) ? 1'b0 : 1'b1; // rev8 + + default: illegal_insn = 1'b1; + endcase + end + end + 5'b0_0101: begin + if ((RV32B == RV32BFull)) begin + illegal_insn = 1'b0; // gorci + end else if (instr[24:20] == 5'b00111) begin + illegal_insn = (RV32B == RV32BBalanced) ? 1'b0 : 1'b1; // orc.b + end + end + 5'b0_0001: begin + if (instr[26] == 1'b0) begin + illegal_insn = (RV32B == RV32BFull) ? 1'b0 : 1'b1; // unshfl + end else begin + illegal_insn = 1'b1; + end + end + + default: illegal_insn = 1'b1; + endcase + end + end + + // default: illegal_insn = 1'b1; + endcase + end + + OPCODE_OP: begin // Register-Register ALU operation + rf_ren_a_o = 1'b1; + rf_ren_b_o = 1'b1; + rf_we = 1'b1; + if ({instr[26], instr[13:12]} == {1'b1, 2'b01}) begin + illegal_insn = (RV32B != RV32BNone) ? 1'b0 : 1'b1; // cmix / cmov / fsl / fsr + end else begin + unique case ({instr[31:25], instr[14:12]}) + // RV32I ALU operations + {7'b000_0000, 3'b000}, + {7'b010_0000, 3'b000}, + {7'b000_0000, 3'b010}, + {7'b000_0000, 3'b011}, + {7'b000_0000, 3'b100}, + {7'b000_0000, 3'b110}, + {7'b000_0000, 3'b111}, + {7'b000_0000, 3'b001}, + {7'b000_0000, 3'b101}, + {7'b010_0000, 3'b101}: illegal_insn = 1'b0; + + // RV32B zbb + {7'b010_0000, 3'b111}, // andn + {7'b010_0000, 3'b110}, // orn + {7'b010_0000, 3'b100}, // xnor + {7'b001_0000, 3'b001}, // slo + {7'b001_0000, 3'b101}, // sro + {7'b011_0000, 3'b001}, // rol + {7'b011_0000, 3'b101}, // ror + {7'b000_0101, 3'b100}, // min + {7'b000_0101, 3'b101}, // max + {7'b000_0101, 3'b110}, // minu + {7'b000_0101, 3'b111}, // maxu + {7'b000_0100, 3'b100}, // pack + {7'b010_0100, 3'b100}, // packu + {7'b000_0100, 3'b111}, // packh + // RV32B zbs + {7'b010_0100, 3'b001}, // sbclr + {7'b001_0100, 3'b001}, // sbset + {7'b011_0100, 3'b001}, // sbinv + {7'b010_0100, 3'b101}, // sbext + // RV32B zbf + {7'b010_0100, 3'b111}: illegal_insn = (RV32B != RV32BNone) ? 1'b0 : 1'b1; // bfp + // RV32B zbe + {7'b010_0100, 3'b110}, // bdep + {7'b000_0100, 3'b110}, // bext + // RV32B zbp + {7'b011_0100, 3'b101}, // grev + {7'b001_0100, 3'b101}, // gorc + {7'b000_0100, 3'b001}, // shfl + {7'b000_0100, 3'b101}, // unshfl + // RV32B zbc + {7'b000_0101, 3'b001}, // clmul + {7'b000_0101, 3'b010}, // clmulr + {7'b000_0101, 3'b011}: illegal_insn = (RV32B == RV32BFull) ? 1'b0 : 1'b1; // clmulh + + // RV32M instructions + {7'b000_0001, 3'b000}: begin // mul + multdiv_operator_o = MD_OP_MULL; + multdiv_signed_mode_o = 2'b00; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b001}: begin // mulh + multdiv_operator_o = MD_OP_MULH; + multdiv_signed_mode_o = 2'b11; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b010}: begin // mulhsu + multdiv_operator_o = MD_OP_MULH; + multdiv_signed_mode_o = 2'b01; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b011}: begin // mulhu + multdiv_operator_o = MD_OP_MULH; + multdiv_signed_mode_o = 2'b00; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b100}: begin // div + multdiv_operator_o = MD_OP_DIV; + multdiv_signed_mode_o = 2'b11; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b101}: begin // divu + multdiv_operator_o = MD_OP_DIV; + multdiv_signed_mode_o = 2'b00; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b110}: begin // rem + multdiv_operator_o = MD_OP_REM; + multdiv_signed_mode_o = 2'b11; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + {7'b000_0001, 3'b111}: begin // remu + multdiv_operator_o = MD_OP_REM; + multdiv_signed_mode_o = 2'b00; + illegal_insn = (RV32M == RV32MNone) ? 1'b1 : 1'b0; + end + default: begin + illegal_insn = 1'b1; + end + endcase + end + end + + ///////////// + // Special // + ///////////// + + OPCODE_MISC_MEM: begin + unique case (instr[14:12]) + 3'b000: begin + // FENCE is treated as a NOP since all memory operations are already strictly ordered. + rf_we = 1'b0; + end + 3'b001: begin + // FENCE.I is implemented as a jump to the next PC, this gives the required flushing + // behaviour (iside prefetch buffer flushed and response to any outstanding iside + // requests will be ignored). + // If present, the ICache will also be flushed. + jump_in_dec_o = 1'b1; + + rf_we = 1'b0; + + if (instr_first_cycle_i) begin + jump_set_o = 1'b1; + icache_inval_o = 1'b1; + end + end + default: begin + illegal_insn = 1'b1; + end + endcase + end + + OPCODE_SYSTEM: begin + if (instr[14:12] == 3'b000) begin + // non CSR related SYSTEM instructions + unique case (instr[31:20]) + 12'h000: // ECALL + // environment (system) call + ecall_insn_o = 1'b1; + + 12'h001: // ebreak + // debugger trap + ebrk_insn_o = 1'b1; + + 12'h302: // mret + mret_insn_o = 1'b1; + + 12'h7b2: // dret + dret_insn_o = 1'b1; + + 12'h105: // wfi + wfi_insn_o = 1'b1; + + default: + illegal_insn = 1'b1; + endcase + + // rs1 and rd must be 0 + if (instr_rs1 != 5'b0 || instr_rd != 5'b0) begin + illegal_insn = 1'b1; + end + end else begin + // instruction to read/modify CSR + csr_access_o = 1'b1; + rf_wdata_sel_o = RF_WD_CSR; + rf_we = 1'b1; + + if (~instr[14]) begin + rf_ren_a_o = 1'b1; + end + + unique case (instr[13:12]) + 2'b01: csr_op = CSR_OP_WRITE; + 2'b10: csr_op = CSR_OP_SET; + 2'b11: csr_op = CSR_OP_CLEAR; + default: csr_illegal = 1'b1; + endcase + + illegal_insn = csr_illegal; + end + + end + + ////////////////////////////////////////// + // Floating Point Extension (F and D) // + ////////////////////////////////////////// + + OPCODE_STORE_FP: begin + data_req_o = 1'b1; + data_we_o = 1'b1; + data_type_o = 2'b00; + + use_fp_rs2_o = 1'b1; + + unique case(instr[14:12]) + 3'b011: begin // FSD + illegal_insn = (RVF == RV64FDouble) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + 3'b010: begin // FSW + illegal_insn = (RVF == RV32FNone) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + default: illegal_insn = 1'b1; + endcase + end + + OPCODE_LOAD_FP: begin + data_req_o = 1'b1; + data_type_o = 2'b00; + fp_load_o = 1'b1; + + use_fp_rd_o = 1'b1; + + unique case(instr[14:12]) + 3'b011: begin // FLD + illegal_insn = (RVF == RV64FDouble) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + 3'b010: begin // FLW + illegal_insn = (RVF == RV32FNone) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + default: illegal_insn = 1'b1; + endcase + end + + OPCODE_MADD_FP, // FMADD.S, FMADD.D + OPCODE_MSUB_FP, // FMSUB.S, FMSUB.D + OPCODE_NMSUB_FP, // FNMSUB.S, FNMSUB.D + OPCODE_NMADD_FP: begin //FNMADD.S, FNMADD.S + fp_rf_we_o = 1'b1; + fp_src_fmt_o = FP32; + is_fp_instr_o = 1'b1; + + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rs3_o = 1'b1; + use_fp_rd_o = 1'b1; + + unique case (instr[26:25]) + 01: begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + 00: begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + default: illegal_insn = 1'b1; + endcase + end + + OPCODE_OP_FP: begin + fp_src_fmt_o = FP32; + is_fp_instr_o = 1'b1; + + unique case (instr[31:25]) + 7'b0000001, // FADD.D + 7'b0000101: begin // FSUB.D + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + fp_swap_oprnds_o = 1'b1; + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + 7'b0001001, // FMUL.D + 7'b0001101:begin // FDIV.D + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + 7'b0000000, // FADD.S + 7'b0000100: begin // FSUB.S + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + fp_swap_oprnds_o = 1'b1; + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + 7'b0001000, // FMUL.S + 7'b0001100: begin // FDIV.S + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + 7'b0101101: begin + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~|instr[24:20]) begin //FSQRT.D + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b0101100: begin // FSQRT.S + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~|instr[24:20]) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + 7'b0010001: begin // FSGNJ.D, FSGNJN.D, FSGNJX.D + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~(instr[14] | (&instr[13:12]))) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b0010000: begin // FSGNJ.S, FSGNJN.S, FSGNJX.S + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~(instr[14] | (&instr[13:12]))) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + 7'b0010101: begin // FMIN.D, FMAX.D + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~|instr[14:13]) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b0010100: begin // FMIN.S, FMAX.S + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~|instr[14:13]) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + 7'b0100000: begin // FCVT.S.D + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~(|instr[24:21] | (~instr[20]))) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b1100000: begin // FCVT.W.S, FCVT.WU.S + rf_we = 1'b1; // write back in int_regfile + use_fp_rs1_o = 1'b1; + if (~|instr[24:21]) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + 7'b0100001: begin // FCVT.D.S + fp_rf_we_o = 1'b1; + use_fp_rs1_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~|instr[24:20]) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b1110000: begin // FMV.X.W , FCLASS.S + rf_we = 1'b1; // write back in int_regfile + unique case ({instr[24:20],instr[14:12]}) + {5'b00000,3'b000}: begin + use_fp_rs1_o = 1'b1; + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + mv_instr_o = 1'b1; + end + {5'b00000,3'b001}: begin + use_fp_rs1_o = 1'b1; + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + default: begin + illegal_insn =1'b1; + end + endcase + end + 7'b1010001: begin // FEQ.D, FLT.D, FLE.D + rf_we = 1'b1; // write back in int_regfile + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + if (~(instr[14]) | (&instr[13:12])) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b1010000: begin // FEQ.S, FLT.S, FLE.S + rf_we = 1'b1; // write back in int_regfile + use_fp_rs1_o = 1'b1; + use_fp_rs2_o = 1'b1; + if (~(instr[14]) | (&instr[13:12])) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + 7'b1110001: begin // FCLASS.D + rf_we = 1'b1; // write back in int_regfile + use_fp_rs1_o = 1'b1; + unique case ({instr[24:20],instr[14:12]}) + {5'b00000,3'b001}: begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + default: begin + illegal_insn =1'b1; + end + endcase + end + 7'b1100001: begin // // FCVT.W.D, FCVT.WU.D + rf_we = 1'b1; // write back in int_regfile + use_fp_rs1_o = 1'b1; + if (~|instr[24:21]) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b1101000: begin // FCVT.S.W, FCVT.S.WU + fp_rf_we_o = 1'b1; + use_fp_rd_o = 1'b1; + if (~|instr[24:21]) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + 7'b1111001: begin // FCVT.D.W, FCVT.D.WU + rf_we = 1'b1; // write back in int_regfile + use_fp_rd_o = 1'b1; + if (~|instr[24:21]) begin + illegal_insn = ((RVF == RV64FDouble) & (fp_invalid_rm)) ? 1'b0 : 1'b1; + fp_src_fmt_o = FP64; + end + end + 7'b1111000: begin // FMV.W.X + fp_rf_we_o = 1'b1; + use_fp_rd_o = 1'b1; + mv_instr_o = 1'b1; + if (~(|instr[24:20]) | (|instr[14:12])) begin + illegal_insn = ((RVF == RV32FNone) & (~fp_invalid_rm)) ? 1'b1 : 1'b0; + fp_src_fmt_o = FP32; + end + end + default: illegal_insn = 1'b1; + endcase + end + default: begin + illegal_insn = 1'b1; + end + endcase + + // make sure illegal compressed instructions cause illegal instruction exceptions + if (illegal_c_insn_i) begin + illegal_insn = 1'b1; + end + + // make sure illegal instructions detected in the decoder do not propagate from decoder + // into register file, LSU, EX, WB, CSRs, PC + // NOTE: instructions can also be detected to be illegal inside the CSRs (upon accesses with + // insufficient privileges), or when accessing non-available registers in RV32E, + // these cases are not handled here + if (illegal_insn) begin + rf_we = 1'b0; + data_req_o = 1'b0; + data_we_o = 1'b0; + jump_in_dec_o = 1'b0; + jump_set_o = 1'b0; + branch_in_dec_o = 1'b0; + csr_access_o = 1'b0; + + // floating point + fp_rf_we_o = 1'b0; + end + end + + ///////////////////////////// + // Decoder for ALU control // + ///////////////////////////// + + always_comb begin + alu_operator_o = ALU_SLTU; + alu_op_a_mux_sel_o = OP_A_IMM; + alu_op_b_mux_sel_o = OP_B_IMM; + + imm_a_mux_sel_o = IMM_A_ZERO; + imm_b_mux_sel_o = IMM_B_I; + + bt_a_mux_sel_o = OP_A_CURRPC; + bt_b_mux_sel_o = IMM_B_I; + + + opcode_alu = opcode_e'(instr_alu[6:0]); + + use_rs3_d = 1'b0; + alu_multicycle_o = 1'b0; + mult_sel_o = 1'b0; + div_sel_o = 1'b0; + + fp_alu_op_mod_o = 1'b0; + fp_alu_operator_o = FMADD; + fp_alu_op_b_mux_sel_o = OP_B_IMM; // op_b_sel_e, OP_B_REG_B + + unique case (opcode_alu) + + /////////// + // Jumps // + /////////// + + OPCODE_JAL: begin // Jump and Link + if (BranchTargetALU) begin + bt_a_mux_sel_o = OP_A_CURRPC; + bt_b_mux_sel_o = IMM_B_J; + end + + // Jumps take two cycles without the BTALU + if (instr_first_cycle_i && !BranchTargetALU) begin + // Calculate jump target + alu_op_a_mux_sel_o = OP_A_CURRPC; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_J; + alu_operator_o = ALU_ADD; + end else begin + // Calculate and store PC+4 + alu_op_a_mux_sel_o = OP_A_CURRPC; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_INCR_PC; + alu_operator_o = ALU_ADD; + end + end + + OPCODE_JALR: begin // Jump and Link Register + if (BranchTargetALU) begin + bt_a_mux_sel_o = OP_A_REG_A; + bt_b_mux_sel_o = IMM_B_I; + end + + // Jumps take two cycles without the BTALU + if (instr_first_cycle_i && !BranchTargetALU) begin + // Calculate jump target + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_I; + alu_operator_o = ALU_ADD; + end else begin + // Calculate and store PC+4 + alu_op_a_mux_sel_o = OP_A_CURRPC; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_INCR_PC; + alu_operator_o = ALU_ADD; + end + end + + OPCODE_BRANCH: begin // Branch + // Check branch condition selection + unique case (instr_alu[14:12]) + 3'b000: alu_operator_o = ALU_EQ; + 3'b001: alu_operator_o = ALU_NE; + 3'b100: alu_operator_o = ALU_LT; + 3'b101: alu_operator_o = ALU_GE; + 3'b110: alu_operator_o = ALU_LTU; + 3'b111: alu_operator_o = ALU_GEU; + default: ; + endcase + + if (BranchTargetALU) begin + bt_a_mux_sel_o = OP_A_CURRPC; + // Not-taken branch will jump to next instruction (used in secure mode) + bt_b_mux_sel_o = branch_taken_i ? IMM_B_B : IMM_B_INCR_PC; + end + + // Without branch target ALU, a branch is a two-stage operation using the Main ALU in both + // stages + if (instr_first_cycle_i) begin + // First evaluate the branch condition + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_REG_B; + end else begin + // Then calculate jump target + alu_op_a_mux_sel_o = OP_A_CURRPC; + alu_op_b_mux_sel_o = OP_B_IMM; + // Not-taken branch will jump to next instruction (used in secure mode) + imm_b_mux_sel_o = branch_taken_i ? IMM_B_B : IMM_B_INCR_PC; + alu_operator_o = ALU_ADD; + end + end + + //////////////// + // Load/store // + //////////////// + + OPCODE_STORE: begin + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_REG_B; + alu_operator_o = ALU_ADD; + + if (!instr_alu[14]) begin + // offset from immediate + imm_b_mux_sel_o = IMM_B_S; + alu_op_b_mux_sel_o = OP_B_IMM; + end + end + + OPCODE_LOAD: begin + alu_op_a_mux_sel_o = OP_A_REG_A; + + // offset from immediate + alu_operator_o = ALU_ADD; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_I; + end + + ///////// + // ALU // + ///////// + + OPCODE_LUI: begin // Load Upper Immediate + alu_op_a_mux_sel_o = OP_A_IMM; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_a_mux_sel_o = IMM_A_ZERO; + imm_b_mux_sel_o = IMM_B_U; + alu_operator_o = ALU_ADD; + end + + OPCODE_AUIPC: begin // Add Upper Immediate to PC + alu_op_a_mux_sel_o = OP_A_CURRPC; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_U; + alu_operator_o = ALU_ADD; + end + + OPCODE_OP_IMM: begin // Register-Immediate ALU Operations + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_I; + + unique case (instr_alu[14:12]) + 3'b000: alu_operator_o = ALU_ADD; // Add Immediate + 3'b010: alu_operator_o = ALU_SLT; // Set to one if Lower Than Immediate + 3'b011: alu_operator_o = ALU_SLTU; // Set to one if Lower Than Immediate Unsigned + 3'b100: alu_operator_o = ALU_XOR; // Exclusive Or with Immediate + 3'b110: alu_operator_o = ALU_OR; // Or with Immediate + 3'b111: alu_operator_o = ALU_AND; // And with Immediate + + 3'b001: begin + if (RV32B != RV32BNone) begin + unique case (instr_alu[31:27]) + 5'b0_0000: alu_operator_o = ALU_SLL; // Shift Left Logical by Immediate + 5'b0_0100: alu_operator_o = ALU_SLO; // Shift Left Ones by Immediate + 5'b0_1001: alu_operator_o = ALU_SBCLR; // Clear bit specified by immediate + 5'b0_0101: alu_operator_o = ALU_SBSET; // Set bit specified by immediate + 5'b0_1101: alu_operator_o = ALU_SBINV; // Invert bit specified by immediate. + // Shuffle with Immediate Control Value + 5'b0_0001: if (instr_alu[26] == 0) alu_operator_o = ALU_SHFL; + 5'b0_1100: begin + unique case (instr_alu[26:20]) + 7'b000_0000: alu_operator_o = ALU_CLZ; // clz + 7'b000_0001: alu_operator_o = ALU_CTZ; // ctz + 7'b000_0010: alu_operator_o = ALU_PCNT; // pcnt + 7'b000_0100: alu_operator_o = ALU_SEXTB; // sext.b + 7'b000_0101: alu_operator_o = ALU_SEXTH; // sext.h + 7'b001_0000: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_CRC32_B; // crc32.b + alu_multicycle_o = 1'b1; + end + end + 7'b001_0001: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_CRC32_H; // crc32.h + alu_multicycle_o = 1'b1; + end + end + 7'b001_0010: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_CRC32_W; // crc32.w + alu_multicycle_o = 1'b1; + end + end + 7'b001_1000: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_CRC32C_B; // crc32c.b + alu_multicycle_o = 1'b1; + end + end + 7'b001_1001: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_CRC32C_H; // crc32c.h + alu_multicycle_o = 1'b1; + end + end + 7'b001_1010: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_CRC32C_W; // crc32c.w + alu_multicycle_o = 1'b1; + end + end + default: ; + endcase + end + + default: ; + endcase + end else begin + alu_operator_o = ALU_SLL; // Shift Left Logical by Immediate + end + end + + 3'b101: begin + if (RV32B != RV32BNone) begin + if (instr_alu[26] == 1'b1) begin + alu_operator_o = ALU_FSR; + alu_multicycle_o = 1'b1; + if (instr_first_cycle_i) begin + use_rs3_d = 1'b1; + end else begin + use_rs3_d = 1'b0; + end + end else begin + unique case (instr_alu[31:27]) + 5'b0_0000: alu_operator_o = ALU_SRL; // Shift Right Logical by Immediate + 5'b0_1000: alu_operator_o = ALU_SRA; // Shift Right Arithmetically by Immediate + 5'b0_0100: alu_operator_o = ALU_SRO; // Shift Right Ones by Immediate + 5'b0_1001: alu_operator_o = ALU_SBEXT; // Extract bit specified by immediate. + 5'b0_1100: begin + alu_operator_o = ALU_ROR; // Rotate Right by Immediate + alu_multicycle_o = 1'b1; + end + 5'b0_1101: alu_operator_o = ALU_GREV; // General Reverse with Imm Control Val + 5'b0_0101: alu_operator_o = ALU_GORC; // General Or-combine with Imm Control Val + // Unshuffle with Immediate Control Value + 5'b0_0001: begin + if (RV32B == RV32BFull) begin + if (instr_alu[26] == 1'b0) alu_operator_o = ALU_UNSHFL; + end + end + default: ; + endcase + end + + end else begin + if (instr_alu[31:27] == 5'b0_0000) begin + alu_operator_o = ALU_SRL; // Shift Right Logical by Immediate + end else if (instr_alu[31:27] == 5'b0_1000) begin + alu_operator_o = ALU_SRA; // Shift Right Arithmetically by Immediate + end + end + end + + // default: ; + endcase + end + + OPCODE_OP: begin // Register-Register ALU operation + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_REG_B; + + if (instr_alu[26]) begin + if (RV32B != RV32BNone) begin + unique case ({instr_alu[26:25], instr_alu[14:12]}) + {2'b11, 3'b001}: begin + alu_operator_o = ALU_CMIX; // cmix + alu_multicycle_o = 1'b1; + if (instr_first_cycle_i) begin + use_rs3_d = 1'b1; + end else begin + use_rs3_d = 1'b0; + end + end + {2'b11, 3'b101}: begin + alu_operator_o = ALU_CMOV; // cmov + alu_multicycle_o = 1'b1; + if (instr_first_cycle_i) begin + use_rs3_d = 1'b1; + end else begin + use_rs3_d = 1'b0; + end + end + {2'b10, 3'b001}: begin + alu_operator_o = ALU_FSL; // fsl + alu_multicycle_o = 1'b1; + if (instr_first_cycle_i) begin + use_rs3_d = 1'b1; + end else begin + use_rs3_d = 1'b0; + end + end + {2'b10, 3'b101}: begin + alu_operator_o = ALU_FSR; // fsr + alu_multicycle_o = 1'b1; + if (instr_first_cycle_i) begin + use_rs3_d = 1'b1; + end else begin + use_rs3_d = 1'b0; + end + end + default: ; + endcase + end + end else begin + unique case ({instr_alu[31:25], instr_alu[14:12]}) + // RV32I ALU operations + {7'b000_0000, 3'b000}: alu_operator_o = ALU_ADD; // Add + {7'b010_0000, 3'b000}: alu_operator_o = ALU_SUB; // Sub + {7'b000_0000, 3'b010}: alu_operator_o = ALU_SLT; // Set Lower Than + {7'b000_0000, 3'b011}: alu_operator_o = ALU_SLTU; // Set Lower Than Unsigned + {7'b000_0000, 3'b100}: alu_operator_o = ALU_XOR; // Xor + {7'b000_0000, 3'b110}: alu_operator_o = ALU_OR; // Or + {7'b000_0000, 3'b111}: alu_operator_o = ALU_AND; // And + {7'b000_0000, 3'b001}: alu_operator_o = ALU_SLL; // Shift Left Logical + {7'b000_0000, 3'b101}: alu_operator_o = ALU_SRL; // Shift Right Logical + {7'b010_0000, 3'b101}: alu_operator_o = ALU_SRA; // Shift Right Arithmetic + + // RV32B ALU Operations + {7'b001_0000, 3'b001}: if (RV32B != RV32BNone) alu_operator_o = ALU_SLO; // slo + {7'b001_0000, 3'b101}: if (RV32B != RV32BNone) alu_operator_o = ALU_SRO; // sro + {7'b011_0000, 3'b001}: begin + if (RV32B != RV32BNone) begin + alu_operator_o = ALU_ROL; // rol + alu_multicycle_o = 1'b1; + end + end + {7'b011_0000, 3'b101}: begin + if (RV32B != RV32BNone) begin + alu_operator_o = ALU_ROR; // ror + alu_multicycle_o = 1'b1; + end + end + + {7'b000_0101, 3'b100}: if (RV32B != RV32BNone) alu_operator_o = ALU_MIN; // min + {7'b000_0101, 3'b101}: if (RV32B != RV32BNone) alu_operator_o = ALU_MAX; // max + {7'b000_0101, 3'b110}: if (RV32B != RV32BNone) alu_operator_o = ALU_MINU; // minu + {7'b000_0101, 3'b111}: if (RV32B != RV32BNone) alu_operator_o = ALU_MAXU; // maxu + + {7'b000_0100, 3'b100}: if (RV32B != RV32BNone) alu_operator_o = ALU_PACK; // pack + {7'b010_0100, 3'b100}: if (RV32B != RV32BNone) alu_operator_o = ALU_PACKU; // packu + {7'b000_0100, 3'b111}: if (RV32B != RV32BNone) alu_operator_o = ALU_PACKH; // packh + + {7'b010_0000, 3'b100}: if (RV32B != RV32BNone) alu_operator_o = ALU_XNOR; // xnor + {7'b010_0000, 3'b110}: if (RV32B != RV32BNone) alu_operator_o = ALU_ORN; // orn + {7'b010_0000, 3'b111}: if (RV32B != RV32BNone) alu_operator_o = ALU_ANDN; // andn + + // RV32B zbs + {7'b010_0100, 3'b001}: if (RV32B != RV32BNone) alu_operator_o = ALU_SBCLR; // sbclr + {7'b001_0100, 3'b001}: if (RV32B != RV32BNone) alu_operator_o = ALU_SBSET; // sbset + {7'b011_0100, 3'b001}: if (RV32B != RV32BNone) alu_operator_o = ALU_SBINV; // sbinv + {7'b010_0100, 3'b101}: if (RV32B != RV32BNone) alu_operator_o = ALU_SBEXT; // sbext + + // RV32B zbf + {7'b010_0100, 3'b111}: if (RV32B != RV32BNone) alu_operator_o = ALU_BFP; // bfp + + // RV32B zbp + {7'b011_0100, 3'b101}: if (RV32B != RV32BNone) alu_operator_o = ALU_GREV; // grev + {7'b001_0100, 3'b101}: if (RV32B != RV32BNone) alu_operator_o = ALU_GORC; // grev + {7'b000_0100, 3'b001}: if (RV32B == RV32BFull) alu_operator_o = ALU_SHFL; // shfl + {7'b000_0100, 3'b101}: if (RV32B == RV32BFull) alu_operator_o = ALU_UNSHFL; // unshfl + + // RV32B zbc + {7'b000_0101, 3'b001}: if (RV32B == RV32BFull) alu_operator_o = ALU_CLMUL; // clmul + {7'b000_0101, 3'b010}: if (RV32B == RV32BFull) alu_operator_o = ALU_CLMULR; // clmulr + {7'b000_0101, 3'b011}: if (RV32B == RV32BFull) alu_operator_o = ALU_CLMULH; // clmulh + + // RV32B zbe + {7'b010_0100, 3'b110}: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_BDEP; // bdep + alu_multicycle_o = 1'b1; + end + end + {7'b000_0100, 3'b110}: begin + if (RV32B == RV32BFull) begin + alu_operator_o = ALU_BEXT; // bext + alu_multicycle_o = 1'b1; + end + end + + // RV32M instructions, all use the same ALU operation + {7'b000_0001, 3'b000}: begin // mul + alu_operator_o = ALU_ADD; + mult_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b001}: begin // mulh + alu_operator_o = ALU_ADD; + mult_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b010}: begin // mulhsu + alu_operator_o = ALU_ADD; + mult_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b011}: begin // mulhu + alu_operator_o = ALU_ADD; + mult_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b100}: begin // div + alu_operator_o = ALU_ADD; + div_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b101}: begin // divu + alu_operator_o = ALU_ADD; + div_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b110}: begin // rem + alu_operator_o = ALU_ADD; + div_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + {7'b000_0001, 3'b111}: begin // remu + alu_operator_o = ALU_ADD; + div_sel_o = (RV32M == RV32MNone) ? 1'b0 : 1'b1; + end + + default: ; + endcase + end + end + + ///////////// + // Special // + ///////////// + + OPCODE_MISC_MEM: begin + unique case (instr_alu[14:12]) + 3'b000: begin + // FENCE is treated as a NOP since all memory operations are already strictly ordered. + alu_operator_o = ALU_ADD; // nop + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_IMM; + end + 3'b001: begin + // FENCE.I will flush the IF stage, prefetch buffer and ICache if present. + if (BranchTargetALU) begin + bt_a_mux_sel_o = OP_A_CURRPC; + bt_b_mux_sel_o = IMM_B_INCR_PC; + end else begin + alu_op_a_mux_sel_o = OP_A_CURRPC; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_INCR_PC; + alu_operator_o = ALU_ADD; + end + end + default: ; + endcase + end + + OPCODE_SYSTEM: begin + if (instr_alu[14:12] == 3'b000) begin + // non CSR related SYSTEM instructions + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_IMM; + end else begin + // instruction to read/modify CSR + alu_op_b_mux_sel_o = OP_B_IMM; + imm_a_mux_sel_o = IMM_A_Z; + imm_b_mux_sel_o = IMM_B_I; // CSR address is encoded in I imm + + if (instr_alu[14]) begin + // rs1 field is used as immediate + alu_op_a_mux_sel_o = OP_A_IMM; + end else begin + alu_op_a_mux_sel_o = OP_A_REG_A; + end + end + end + ////////////////////////////////////////// + // Floating Point Extension (F and D) // + ////////////////////////////////////////// + + OPCODE_STORE_FP: begin + alu_op_a_mux_sel_o = OP_A_REG_A; + alu_op_b_mux_sel_o = OP_B_REG_B; + alu_operator_o = ALU_ADD; + + unique case(instr[14:12]) + 3'b011: begin // FSD + imm_b_mux_sel_o = IMM_B_S; + alu_op_b_mux_sel_o = OP_B_IMM; + end + 3'b010: begin // FSW + imm_b_mux_sel_o = IMM_B_S; + alu_op_b_mux_sel_o = OP_B_IMM; + end + default: ; + endcase + end + + OPCODE_LOAD_FP: begin + unique case(instr[14:12]) + 3'b011: begin // FLD + alu_op_a_mux_sel_o = OP_A_REG_A; + + alu_operator_o = ALU_ADD; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_I; + end + 3'b010: begin // FLW + alu_op_a_mux_sel_o = OP_A_REG_A; + + alu_operator_o = ALU_ADD; + alu_op_b_mux_sel_o = OP_B_IMM; + imm_b_mux_sel_o = IMM_B_I; + end + default: ; + endcase + end + + OPCODE_MADD_FP: begin // FMADD.S, FMADD.D + unique case (instr[26:25]) + 01: begin + fp_alu_operator_o = FMADD; + fp_alu_op_mod_o = 1'b0; + end + 00: begin + fp_alu_operator_o = FMADD; + fp_alu_op_mod_o = 1'b0; + end + default: ; + endcase + end + + OPCODE_MSUB_FP: begin // FMSUB.S, FMSUB.D + unique case (instr[26:25]) + 01: begin + fp_alu_operator_o = FMADD; + fp_alu_op_mod_o = 1'b1; + end + 00: begin + fp_alu_operator_o = FMADD; + fp_alu_op_mod_o = 1'b1; + end + default: ; + endcase + end + + OPCODE_NMSUB_FP: begin // FNMSUB.S, FNMSUB.D + unique case (instr[26:25]) + 01: begin + fp_alu_operator_o = FNMSUB; + end + 00: begin + fp_alu_operator_o = FNMSUB; + end + default: ; + endcase + end + + OPCODE_NMADD_FP: begin //FNMADD.S, FNMADD.S + unique case (instr[26:25]) + 01: begin + fp_alu_operator_o = FNMSUB; + fp_alu_op_mod_o = 1'b1; + end + 00: begin + fp_alu_operator_o = FNMSUB; + fp_alu_op_mod_o = 1'b1; + end + default: ; + endcase + end + + OPCODE_OP_FP: begin + unique case (instr[31:25]) + 7'b0000001: begin // FADD.D + fp_alu_operator_o = ADD; + end + 7'b0000101: begin // FSUB.D + fp_alu_operator_o = ADD; + fp_alu_op_mod_o = 1'b1; + end + 7'b0001001: begin // FMUL.D + fp_alu_operator_o = MUL; + end + 7'b0001101:begin // FDIV.S + fp_alu_operator_o = DIV; + end + 7'b0000000: begin // FADD.S + fp_alu_operator_o = ADD; + end + 7'b0000100: begin // FSUB.S + fp_alu_operator_o = ADD; + fp_alu_op_mod_o = 1'b1; + end + 7'b0001000: begin // FMUL.S + fp_alu_operator_o = MUL; + end + 7'b0001100: begin // FDIV.S + fp_alu_operator_o = DIV; + end + 7'b0101101: begin + if (~|instr[24:20]) begin // FSQRT.D + fp_alu_operator_o = SQRT; + end + end + 7'b0101100: begin // FSQRT.S + if (~|instr[24:20]) begin + fp_alu_operator_o = SQRT; + end + end + 7'b0010001: begin // FSGNJ.D, FSGNJN.D, FSGNJX.D + if (~(instr[14] | (&instr[13:12]))) begin + fp_alu_operator_o = SGNJ; + end + end + 7'b0010000: begin // FSGNJ.S, FSGNJN.S, FSGNJX.S + if (~(instr[14] | (&instr[13:12]))) begin + fp_alu_operator_o = SGNJ; + end + end + 7'b0010101: begin // FMIN.D, FMAX.D + if (~|instr[14:13]) begin + fp_alu_operator_o = MINMAX; + end + end + 7'b0010100: begin // FMIN.S, FMAX.S + if (~|instr[14:13]) begin + fp_alu_operator_o = MINMAX; + end + end + 7'b0100000: begin // FCVT.S.D + if (~(|instr[24:21] | (~instr[20]))) begin + fp_alu_operator_o = F2F; + end + end + 7'b1100000: begin // FCVT.W.S, FCVT.WU.S + if (~|instr[24:21]) begin + fp_alu_operator_o = F2I; + + if (instr[20]) + fp_alu_op_mod_o = 1'b1; + end + end + 7'b0100001: begin // FCVT.D.S + if (~|instr[24:20]) begin + fp_alu_operator_o = F2F; + end + end + 7'b1110000: begin // FMV.X.W , FCLASS.S + unique case ({instr[24:20],instr[14:12]}) + // {3'b0000000,3'b000}: begin + // fp_alu_operator_o = ADD; // to be decided YET + // end + {3'b000,3'b001}: begin + fp_alu_operator_o = CLASSIFY; + end + default: ; + endcase + end + 7'b1010001: begin // FEQ.D, FLT.D, FLE.D + if ((~instr[14]) | (&instr[13:12])) begin + fp_alu_operator_o = CMP; + end + end + 7'b1010000: begin // FEQ.S, FLT.S, FLE.S + if ((~instr[14]) | (&instr[13:12])) begin + fp_alu_operator_o = CMP; + end + end + 7'b1110001: begin // FCLASS.D + unique case ({instr[24:20],instr[14:12]}) + {3'b000,3'b001}: begin + fp_alu_operator_o = CLASSIFY; + end + default: ; + endcase + end + 7'b1100001: begin // // FCVT.W.D, FCVT.WU.D + if (~|instr[24:21]) begin + fp_alu_operator_o = F2I; + + if (instr[20]) + fp_alu_op_mod_o = 1'b1; + end + end + 7'b1101000: begin // FCVT.S.W, FCVT.S.WU + if (~(|instr[24:21])) begin + fp_alu_operator_o = I2F; + + if (instr[20]) + fp_alu_op_mod_o = 1'b1; + end + end + 7'b1111001: begin // FCVT.D.W, FCVT.D.WU + if (~|instr[24:21]) begin + fp_alu_operator_o = I2F; + + if (instr[20]) + fp_alu_op_mod_o = 1'b1; + end + end + // 7'b1111000: begin // FMV.W.X + // if ((|instr[24:20]) | (|instr[14:12])) begin + // fp_alu_operator_o = FMADD; // to be decided + // end + // end + default: ; + endcase + end + default: ; + endcase + end + + // do not enable multdiv in case of illegal instruction exceptions + assign mult_en_o = illegal_insn ? 1'b0 : mult_sel_o; + assign div_en_o = illegal_insn ? 1'b0 : div_sel_o; + + // make sure instructions accessing non-available registers in RV32E cause illegal + // instruction exceptions + assign illegal_insn_o = illegal_insn | illegal_reg_rv32e; + + // do not propgate regfile write enable if non-available registers are accessed in RV32E + assign rf_we_o = rf_we & ~illegal_reg_rv32e; + + //////////////// + // Assertions // + //////////////// + +// // Selectors must be known/valid. +// `ASSERT(buraqRegImmAluOpKnown, (opcode == OPCODE_OP_IMM) |-> +// !$isunknown(instr[14:12])) +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_ifu.sv b/verilog/rtl/brq_ifu.sv new file mode 100644 index 0000000..4331423 --- /dev/null +++ b/verilog/rtl/brq_ifu.sv
@@ -0,0 +1,501 @@ + + +/** + * Instruction Fetch Stage + * + * Instruction fetch unit: Selection of the next PC, and buffering (sampling) of + * the read instruction. + */ + + + +module brq_ifu #( + parameter int unsigned DmHaltAddr = 32'h1A110800, + parameter int unsigned DmExceptionAddr = 32'h1A110808, + parameter bit DummyInstructions = 1'b0, + parameter bit ICache = 1'b0, + parameter bit ICacheECC = 1'b0, + parameter bit PCIncrCheck = 1'b0, + parameter bit BranchPredictor = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + + input logic [31:0] boot_addr_i, // also used for mtvec + input logic req_i, // instruction request control + + // instruction cache interface + output logic instr_req_o, + output logic [31:0] instr_addr_o, + input logic instr_gnt_i, + input logic instr_rvalid_i, + input logic [31:0] instr_rdata_i, + input logic instr_err_i, + input logic instr_pmp_err_i, + + // output of ID stage + output logic instr_valid_id_o, // instr in IF-ID is valid + output logic instr_new_id_o, // instr in IF-ID is new + output logic [31:0] instr_rdata_id_o, // instr for ID stage + output logic [31:0] instr_rdata_alu_id_o, // replicated instr for ID stage + // to reduce fan-out + output logic [15:0] instr_rdata_c_id_o, // compressed instr for ID stage + // (mtval), meaningful only if + // instr_is_compressed_id_o = 1'b1 + output logic instr_is_compressed_id_o, // compressed decoder thinks this + // is a compressed instr + output logic instr_bp_taken_o, // instruction was predicted to be + // a taken branch + output logic instr_fetch_err_o, // bus error on fetch + output logic instr_fetch_err_plus2_o, // bus error misaligned + output logic illegal_c_insn_id_o, // compressed decoder thinks this + // is an invalid instr + output logic dummy_instr_id_o, // Instruction is a dummy + output logic [31:0] pc_if_o, + output logic [31:0] pc_id_o, + + // control signals + input logic instr_valid_clear_i, // clear instr valid bit in IF-ID + input logic pc_set_i, // set the PC to a new value + input logic pc_set_spec_i, + input brq_pkg::pc_sel_e pc_mux_i, // selector for PC multiplexer + input logic nt_branch_mispredict_i, // Not-taken branch in ID/EX was + // mispredicted (predicted taken) + input brq_pkg::exc_pc_sel_e exc_pc_mux_i, // selects ISR address + input brq_pkg::exc_cause_e exc_cause, // selects ISR address for + // vectorized interrupt lines + input logic dummy_instr_en_i, + input logic [2:0] dummy_instr_mask_i, + input logic dummy_instr_seed_en_i, + input logic [31:0] dummy_instr_seed_i, + input logic icache_enable_i, + input logic icache_inval_i, + + // jump and branch target + input logic [31:0] branch_target_ex_i, // branch/jump target address + + // CSRs + input logic [31:0] csr_mepc_i, // PC to restore after handling + // the interrupt/exception + input logic [31:0] csr_depc_i, // PC to restore after handling + // the debug request + input logic [31:0] csr_mtvec_i, // base PC to jump to on exception + output logic csr_mtvec_init_o, // tell CS regfile to init mtvec + + // pipeline stall + input logic id_in_ready_i, // ID stage is ready for new instr + + // misc signals + output logic pc_mismatch_alert_o, + output logic if_busy_o // IF stage is busy fetching instr +); + + import brq_pkg::*; + + logic instr_valid_id_d, instr_valid_id_q; + logic instr_new_id_d, instr_new_id_q; + + // prefetch buffer related signals + logic prefetch_busy; + logic branch_req; + logic branch_spec; + logic predicted_branch; + logic [31:0] fetch_addr_n; + logic unused_fetch_addr_n0; + + logic fetch_valid; + logic fetch_ready; + logic [31:0] fetch_rdata; + logic [31:0] fetch_addr; + logic fetch_err; + logic fetch_err_plus2; + + logic if_instr_valid; + logic [31:0] if_instr_rdata; + logic [31:0] if_instr_addr; + logic if_instr_err; + + logic [31:0] exc_pc; + + logic [5:0] irq_id; + logic unused_irq_bit; + + logic if_id_pipe_reg_we; // IF-ID pipeline reg write enable + + // Dummy instruction signals + logic stall_dummy_instr; + logic [31:0] instr_out; + logic instr_is_compressed_out; + logic illegal_c_instr_out; + logic instr_err_out; + + logic predict_branch_taken; + logic [31:0] predict_branch_pc; + + brq_pkg::pc_sel_e pc_mux_internal; + + logic [7:0] unused_boot_addr; + logic [7:0] unused_csr_mtvec; + + assign unused_boot_addr = boot_addr_i[7:0]; + assign unused_csr_mtvec = csr_mtvec_i[7:0]; + + // extract interrupt ID from exception cause + assign irq_id = {exc_cause}; + assign unused_irq_bit = irq_id[5]; // MSB distinguishes interrupts from exceptions + + // exception PC selection mux + always_comb begin : exc_pc_mux + unique case (exc_pc_mux_i) + EXC_PC_EXC: exc_pc = { csr_mtvec_i[31:2], 2'b00 }; + EXC_PC_IRQ: exc_pc = { csr_mtvec_i[31:2], 2'b00 }; + EXC_PC_DBD: exc_pc = DmHaltAddr; + EXC_PC_DBG_EXC: exc_pc = DmExceptionAddr; + // default: exc_pc = { csr_mtvec_i[31:8], 8'h00 }; + endcase + end + + // The Branch predictor can provide a new PC which is internal to ifu. Only override the mux + // select to choose this if the core isn't already trying to set a PC. + assign pc_mux_internal = + (BranchPredictor && predict_branch_taken && !pc_set_i) ? PC_BP : pc_mux_i; + + // fetch address selection mux + always_comb begin : fetch_addr_mux + unique case (pc_mux_internal) + PC_BOOT: fetch_addr_n = { boot_addr_i[31:2], 2'b00 }; + PC_JUMP: fetch_addr_n = branch_target_ex_i; + PC_EXC: fetch_addr_n = exc_pc; // set PC to exception handler + PC_ERET: fetch_addr_n = csr_mepc_i; // restore PC when returning from EXC + PC_DRET: fetch_addr_n = csr_depc_i; + // Without branch predictor will never get pc_mux_internal == PC_BP. We still handle no branch + // predictor case here to ensure redundant mux logic isn't synthesised. + PC_BP: fetch_addr_n = BranchPredictor ? predict_branch_pc : { boot_addr_i[31:2], 2'b00 }; + default: fetch_addr_n = { boot_addr_i[31:2], 2'b00 }; + endcase + end + + // tell CS register file to initialize mtvec on boot + assign csr_mtvec_init_o = (pc_mux_i == PC_BOOT) & pc_set_i; + + if (ICache) begin : gen_ifu_icache + // Full I-Cache option + brq_ifu_icache #( + .BranchPredictor (BranchPredictor), + .ICacheECC (ICacheECC) + ) icache_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .req_i ( req_i ), + + .branch_i ( branch_req ), + .branch_spec_i ( branch_spec ), + .predicted_branch_i ( predicted_branch ), + .branch_mispredict_i ( nt_branch_mispredict_i ), + .addr_i ( {fetch_addr_n[31:1], 1'b0} ), + + .ready_i ( fetch_ready ), + .valid_o ( fetch_valid ), + .rdata_o ( fetch_rdata ), + .addr_o ( fetch_addr ), + .err_o ( fetch_err ), + .err_plus2_o ( fetch_err_plus2 ), + + .instr_req_o ( instr_req_o ), + .instr_addr_o ( instr_addr_o ), + .instr_gnt_i ( instr_gnt_i ), + .instr_rvalid_i ( instr_rvalid_i ), + .instr_rdata_i ( instr_rdata_i ), + .instr_err_i ( instr_err_i ), + .instr_pmp_err_i ( instr_pmp_err_i ), + + .icache_enable_i ( icache_enable_i ), + .icache_inval_i ( icache_inval_i ), + .busy_o ( prefetch_busy ) + ); + end else begin : gen_ifu_prefetch_buffer + // prefetch buffer, caches a fixed number of instructions + brq_ifu_prefetch_buffer #( + .BranchPredictor (BranchPredictor) + ) ifu_prefetch_buffer_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .req_i ( req_i ), + + .branch_i ( branch_req ), + .branch_spec_i ( branch_spec ), + .predicted_branch_i ( predicted_branch ), + .branch_mispredict_i ( nt_branch_mispredict_i ), + .addr_i ( {fetch_addr_n[31:1], 1'b0} ), + + .ready_i ( fetch_ready ), + .valid_o ( fetch_valid ), + .rdata_o ( fetch_rdata ), + .addr_o ( fetch_addr ), + .err_o ( fetch_err ), + .err_plus2_o ( fetch_err_plus2 ), + + .instr_req_o ( instr_req_o ), + .instr_addr_o ( instr_addr_o ), + .instr_gnt_i ( instr_gnt_i ), + .instr_rvalid_i ( instr_rvalid_i ), + .instr_rdata_i ( instr_rdata_i ), + .instr_err_i ( instr_err_i ), + .instr_pmp_err_i ( instr_pmp_err_i ), + + .busy_o ( prefetch_busy ) + ); + // ICache tieoffs + logic unused_icen, unused_icinv; + assign unused_icen = icache_enable_i; + assign unused_icinv = icache_inval_i; + end + + assign unused_fetch_addr_n0 = fetch_addr_n[0]; + + assign branch_req = pc_set_i | predict_branch_taken; + assign branch_spec = pc_set_spec_i | predict_branch_taken; + + assign pc_if_o = if_instr_addr; + assign if_busy_o = prefetch_busy; + + // compressed instruction decoding, or more precisely compressed instruction + // expander + // + // since it does not matter where we decompress instructions, we do it here + // to ease timing closure + logic [31:0] instr_decompressed; + logic illegal_c_insn; + logic instr_is_compressed; + + brq_ifu_compressed_decoder ifu_compressed_decoder_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .valid_i ( fetch_valid & ~fetch_err ), + .instr_i ( if_instr_rdata ), + .instr_o ( instr_decompressed ), + .is_compressed_o ( instr_is_compressed ), + .illegal_instr_o ( illegal_c_insn ) + ); + + // Dummy instruction insertion + if (DummyInstructions) begin : gen_dummy_instr + logic insert_dummy_instr; + logic [31:0] dummy_instr_data; + + brq_ifu_dummy_instr dummy_instr_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .dummy_instr_en_i ( dummy_instr_en_i ), + .dummy_instr_mask_i ( dummy_instr_mask_i ), + .dummy_instr_seed_en_i ( dummy_instr_seed_en_i ), + .dummy_instr_seed_i ( dummy_instr_seed_i ), + .fetch_valid_i ( fetch_valid ), + .id_in_ready_i ( id_in_ready_i ), + .insert_dummy_instr_o ( insert_dummy_instr ), + .dummy_instr_data_o ( dummy_instr_data ) + ); + + // Mux between actual instructions and dummy instructions + assign instr_out = insert_dummy_instr ? dummy_instr_data : instr_decompressed; + assign instr_is_compressed_out = insert_dummy_instr ? 1'b0 : instr_is_compressed; + assign illegal_c_instr_out = insert_dummy_instr ? 1'b0 : illegal_c_insn; + assign instr_err_out = insert_dummy_instr ? 1'b0 : if_instr_err; + + // Stall the IF stage if we insert a dummy instruction. The dummy will execute between whatever + // is currently in the ID stage and whatever is valid from the prefetch buffer this cycle. The + // PC of the dummy instruction will match whatever is next from the prefetch buffer. + assign stall_dummy_instr = insert_dummy_instr; + + // Register the dummy instruction indication into the ID stage + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + dummy_instr_id_o <= 1'b0; + end else if (if_id_pipe_reg_we) begin + dummy_instr_id_o <= insert_dummy_instr; + end + end + + end else begin : gen_no_dummy_instr + logic unused_dummy_en; + logic [2:0] unused_dummy_mask; + logic unused_dummy_seed_en; + logic [31:0] unused_dummy_seed; + + assign unused_dummy_en = dummy_instr_en_i; + assign unused_dummy_mask = dummy_instr_mask_i; + assign unused_dummy_seed_en = dummy_instr_seed_en_i; + assign unused_dummy_seed = dummy_instr_seed_i; + assign instr_out = instr_decompressed; + assign instr_is_compressed_out = instr_is_compressed; + assign illegal_c_instr_out = illegal_c_insn; + assign instr_err_out = if_instr_err; + assign stall_dummy_instr = 1'b0; + assign dummy_instr_id_o = 1'b0; + end + + // The ID stage becomes valid as soon as any instruction is registered in the ID stage flops. + // Note that the current instruction is squashed by the incoming pc_set_i signal. + // Valid is held until it is explicitly cleared (due to an instruction completing or an exception) + assign instr_valid_id_d = (if_instr_valid & id_in_ready_i & ~pc_set_i) | + (instr_valid_id_q & ~instr_valid_clear_i); + assign instr_new_id_d = if_instr_valid & id_in_ready_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + instr_valid_id_q <= 1'b0; + instr_new_id_q <= 1'b0; + end else begin + instr_valid_id_q <= instr_valid_id_d; + instr_new_id_q <= instr_new_id_d; + end + end + + assign instr_valid_id_o = instr_valid_id_q; + // Signal when a new instruction enters the ID stage (only used for RVFI signalling). + assign instr_new_id_o = instr_new_id_q; + + // IF-ID pipeline registers, frozen when the ID stage is stalled + assign if_id_pipe_reg_we = instr_new_id_d; + + always_ff @(posedge clk_i) begin + if (if_id_pipe_reg_we) begin + instr_rdata_id_o <= instr_out; + // To reduce fan-out and help timing from the instr_rdata_id flops they are replicated. + instr_rdata_alu_id_o <= instr_out; + instr_fetch_err_o <= instr_err_out; + instr_fetch_err_plus2_o <= fetch_err_plus2; + instr_rdata_c_id_o <= if_instr_rdata[15:0]; + instr_is_compressed_id_o <= instr_is_compressed_out; + illegal_c_insn_id_o <= illegal_c_instr_out; + pc_id_o <= pc_if_o; + end + end + + // Check for expected increments of the PC when security hardening enabled + if (PCIncrCheck) begin : g_secure_pc + logic [31:0] prev_instr_addr_incr; + logic prev_instr_seq_q, prev_instr_seq_d; + + // Do not check for sequential increase after a branch, jump, exception, interrupt or debug + // request, all of which will set branch_req. Also do not check after reset or for dummys. + assign prev_instr_seq_d = (prev_instr_seq_q | instr_new_id_d) & + ~branch_req & ~stall_dummy_instr; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + prev_instr_seq_q <= 1'b0; + end else begin + prev_instr_seq_q <= prev_instr_seq_d; + end + end + + assign prev_instr_addr_incr = pc_id_o + ((instr_is_compressed_id_o && !instr_fetch_err_o) ? + 32'd2 : 32'd4); + + // Check that the address equals the previous address +2/+4 + assign pc_mismatch_alert_o = prev_instr_seq_q & (pc_if_o != prev_instr_addr_incr); + + end else begin : g_no_secure_pc + assign pc_mismatch_alert_o = 1'b0; + end + + if (BranchPredictor) begin : g_ifu_branch_predictor + logic [31:0] instr_skid_data_q; + logic [31:0] instr_skid_addr_q; + logic instr_skid_bp_taken_q; + logic instr_skid_valid_q, instr_skid_valid_d; + logic instr_skid_en; + logic instr_bp_taken_q, instr_bp_taken_d; + + logic predict_branch_taken_raw; + + // ID stages needs to know if branch was predicted taken so it can signal mispredicts + always_ff @(posedge clk_i) begin + if (if_id_pipe_reg_we) begin + instr_bp_taken_q <= instr_bp_taken_d; + end + end + + // When branch prediction is enabled a skid buffer between the IF and ID/EX stage is introduced. + // If an instruction in IF is predicted to be a taken branch and ID/EX is not ready the + // instruction in IF is moved to the skid buffer which becomes the output of the IF stage until + // the ID/EX stage accepts the instruction. The skid buffer is required as otherwise the ID/EX + // ready signal is coupled to the instr_req_o output which produces a feedthrough path from + // data_gnt_i -> instr_req_o (which needs to be avoided as for some interconnects this will + // result in a combinational loop). + + assign instr_skid_en = predicted_branch & ~id_in_ready_i & ~instr_skid_valid_q; + + assign instr_skid_valid_d = (instr_skid_valid_q & ~id_in_ready_i & ~stall_dummy_instr) | + instr_skid_en; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + instr_skid_valid_q <= 1'b0; + end else begin + instr_skid_valid_q <= instr_skid_valid_d; + end + end + + always_ff @(posedge clk_i) begin + if (instr_skid_en) begin + instr_skid_bp_taken_q <= predict_branch_taken; + instr_skid_data_q <= fetch_rdata; + instr_skid_addr_q <= fetch_addr; + end + end + + brq_ifu_branch_predict branch_predict_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .fetch_rdata_i ( fetch_rdata ), + .fetch_pc_i ( fetch_addr ), + .fetch_valid_i ( fetch_valid ), + + .predict_branch_taken_o ( predict_branch_taken_raw ), + .predict_branch_pc_o ( predict_branch_pc ) + ); + + // If there is an instruction in the skid buffer there must be no branch prediction. + // Instructions are only placed in the skid after they have been predicted to be a taken branch + // so with the skid valid any prediction has already occurred. + // Do not branch predict on instruction errors. + assign predict_branch_taken = predict_branch_taken_raw & ~instr_skid_valid_q & ~fetch_err; + + // pc_set_i takes precendence over branch prediction + assign predicted_branch = predict_branch_taken & ~pc_set_i; + + assign if_instr_valid = fetch_valid | instr_skid_valid_q; + assign if_instr_rdata = instr_skid_valid_q ? instr_skid_data_q : fetch_rdata; + assign if_instr_addr = instr_skid_valid_q ? instr_skid_addr_q : fetch_addr; + + // Don't branch predict on instruction error so only instructions without errors end up in the + // skid buffer. + assign if_instr_err = ~instr_skid_valid_q & fetch_err; + assign instr_bp_taken_d = instr_skid_valid_q ? instr_skid_bp_taken_q : predict_branch_taken; + + assign fetch_ready = id_in_ready_i & ~stall_dummy_instr & ~instr_skid_valid_q; + + assign instr_bp_taken_o = instr_bp_taken_q; + + + end else begin : g_no_ifu_branch_predictor + assign instr_bp_taken_o = 1'b0; + assign predict_branch_taken = 1'b0; + assign predicted_branch = 1'b0; + assign predict_branch_pc = 32'b0; + + assign if_instr_valid = fetch_valid; + assign if_instr_rdata = fetch_rdata; + assign if_instr_addr = fetch_addr; + assign if_instr_err = fetch_err; + assign fetch_ready = id_in_ready_i & ~stall_dummy_instr; + end + + + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_ifu_compressed_decoder.sv b/verilog/rtl/brq_ifu_compressed_decoder.sv new file mode 100644 index 0000000..1a6fc57 --- /dev/null +++ b/verilog/rtl/brq_ifu_compressed_decoder.sv
@@ -0,0 +1,276 @@ + +/** + * Compressed instruction decoder + * + * Decodes RISC-V compressed instructions into their RV32 equivalent. + * This module is fully combinatorial, clock and reset are used for + * assertions only. + */ + + +module brq_ifu_compressed_decoder ( + input logic clk_i, + input logic rst_ni, + input logic valid_i, + input logic [31:0] instr_i, + output logic [31:0] instr_o, + output logic is_compressed_o, + output logic illegal_instr_o +); + import brq_pkg::*; + + // valid_i indicates if instr_i is valid and is used for assertions only. + // The following signal is used to avoid possible lint errors. + logic unused_valid; + assign unused_valid = valid_i; + + //////////////////////// + // Compressed decoder // + //////////////////////// + + always_comb begin + // By default, forward incoming instruction, mark it as legal. + instr_o = instr_i; + illegal_instr_o = 1'b0; + + // Check if incoming instruction is compressed. + unique case (instr_i[1:0]) + // C0 + 2'b00: begin + unique case (instr_i[15:13]) + 3'b000: begin + // c.addi4spn -> addi rd', x2, imm + instr_o = {2'b0, instr_i[10:7], instr_i[12:11], instr_i[5], + instr_i[6], 2'b00, 5'h02, 3'b000, 2'b01, instr_i[4:2], {OPCODE_OP_IMM}}; + if (instr_i[12:5] == 8'b0) illegal_instr_o = 1'b1; + end + + 3'b010: begin + // c.lw -> lw rd', imm(rs1') + instr_o = {5'b0, instr_i[5], instr_i[12:10], instr_i[6], + 2'b00, 2'b01, instr_i[9:7], 3'b010, 2'b01, instr_i[4:2], {OPCODE_LOAD}}; + end + + 3'b110: begin + // c.sw -> sw rs2', imm(rs1') + instr_o = {5'b0, instr_i[5], instr_i[12], 2'b01, instr_i[4:2], + 2'b01, instr_i[9:7], 3'b010, instr_i[11:10], instr_i[6], + 2'b00, {OPCODE_STORE}}; + end + + 3'b001, + 3'b011, + 3'b100, + 3'b101, + 3'b111: begin + illegal_instr_o = 1'b1; + end + + //default: begin + // illegal_instr_o = 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 + unique case (instr_i[15:13]) + 3'b000: begin + // c.addi -> addi rd, rd, nzimm + // c.nop + instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], + instr_i[11:7], 3'b0, instr_i[11:7], {OPCODE_OP_IMM}}; + end + + 3'b001, 3'b101: begin + // 001: c.jal -> jal x1, imm + // 101: c.j -> jal x0, imm + instr_o = {instr_i[12], instr_i[8], instr_i[10:9], instr_i[6], + instr_i[7], instr_i[2], instr_i[11], instr_i[5:3], + {9 {instr_i[12]}}, 4'b0, ~instr_i[15], {OPCODE_JAL}}; + end + + 3'b010: begin + // c.li -> addi rd, x0, nzimm + // (c.li hints are translated into an addi hint) + instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], 5'b0, + 3'b0, instr_i[11:7], {OPCODE_OP_IMM}}; + end + + 3'b011: begin + // c.lui -> lui rd, imm + // (c.lui hints are translated into a lui hint) + instr_o = {{15 {instr_i[12]}}, instr_i[6:2], instr_i[11:7], {OPCODE_LUI}}; + + if (instr_i[11:7] == 5'h02) begin + // c.addi16sp -> addi x2, x2, nzimm + instr_o = {{3 {instr_i[12]}}, instr_i[4:3], instr_i[5], instr_i[2], + instr_i[6], 4'b0, 5'h02, 3'b000, 5'h02, {OPCODE_OP_IMM}}; + end + + if ({instr_i[12], instr_i[6:2]} == 6'b0) illegal_instr_o = 1'b1; + end + + 3'b100: begin + unique case (instr_i[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) + instr_o = {1'b0, instr_i[10], 5'b0, instr_i[6:2], 2'b01, instr_i[9:7], + 3'b101, 2'b01, instr_i[9:7], {OPCODE_OP_IMM}}; + if (instr_i[12] == 1'b1) illegal_instr_o = 1'b1; + end + + 2'b10: begin + // c.andi -> andi rd, rd, imm + instr_o = {{6 {instr_i[12]}}, instr_i[12], instr_i[6:2], 2'b01, instr_i[9:7], + 3'b111, 2'b01, instr_i[9:7], {OPCODE_OP_IMM}}; + end + + 2'b11: begin + unique case ({instr_i[12], instr_i[6:5]}) + 3'b000: begin + // c.sub -> sub rd', rd', rs2' + instr_o = {2'b01, 5'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], + 3'b000, 2'b01, instr_i[9:7], {OPCODE_OP}}; + end + + 3'b001: begin + // c.xor -> xor rd', rd', rs2' + instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b100, + 2'b01, instr_i[9:7], {OPCODE_OP}}; + end + + 3'b010: begin + // c.or -> or rd', rd', rs2' + instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b110, + 2'b01, instr_i[9:7], {OPCODE_OP}}; + end + + 3'b011: begin + // c.and -> and rd', rd', rs2' + instr_o = {7'b0, 2'b01, instr_i[4:2], 2'b01, instr_i[9:7], 3'b111, + 2'b01, instr_i[9:7], {OPCODE_OP}}; + end + + 3'b100, + 3'b101, + 3'b110, + 3'b111: begin + // 100: c.subw + // 101: c.addw + illegal_instr_o = 1'b1; + end + + // default: begin + // illegal_instr_o = 1'b1; + // end + endcase + end + + // default: begin + // illegal_instr_o = 1'b1; + // end + endcase + end + + 3'b110, 3'b111: begin + // 0: c.beqz -> beq rs1', x0, imm + // 1: c.bnez -> bne rs1', x0, imm + instr_o = {{4 {instr_i[12]}}, instr_i[6:5], instr_i[2], 5'b0, 2'b01, + instr_i[9:7], 2'b00, instr_i[13], instr_i[11:10], instr_i[4:3], + instr_i[12], {OPCODE_BRANCH}}; + end + + // default: begin + // illegal_instr_o = 1'b1; + // 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 + unique case (instr_i[15:13]) + 3'b000: begin + // c.slli -> slli rd, rd, shamt + // (c.ssli hints are translated into a slli hint) + instr_o = {7'b0, instr_i[6:2], instr_i[11:7], 3'b001, instr_i[11:7], {OPCODE_OP_IMM}}; + if (instr_i[12] == 1'b1) illegal_instr_o = 1'b1; // reserved for custom extensions + end + + 3'b010: begin + // c.lwsp -> lw rd, imm(x2) + instr_o = {4'b0, instr_i[3:2], instr_i[12], instr_i[6:4], 2'b00, 5'h02, + 3'b010, instr_i[11:7], OPCODE_LOAD}; + if (instr_i[11:7] == 5'b0) illegal_instr_o = 1'b1; + end + + 3'b100: begin + if (instr_i[12] == 1'b0) begin + if (instr_i[6:2] != 5'b0) begin + // c.mv -> add rd/rs1, x0, rs2 + // (c.mv hints are translated into an add hint) + instr_o = {7'b0, instr_i[6:2], 5'b0, 3'b0, instr_i[11:7], {OPCODE_OP}}; + end else begin + // c.jr -> jalr x0, rd/rs1, 0 + instr_o = {12'b0, instr_i[11:7], 3'b0, 5'b0, {OPCODE_JALR}}; + if (instr_i[11:7] == 5'b0) illegal_instr_o = 1'b1; + end + end else begin + if (instr_i[6:2] != 5'b0) begin + // c.add -> add rd, rd, rs2 + // (c.add hints are translated into an add hint) + instr_o = {7'b0, instr_i[6:2], instr_i[11:7], 3'b0, instr_i[11:7], {OPCODE_OP}}; + end else begin + if (instr_i[11:7] == 5'b0) begin + // c.ebreak -> ebreak + instr_o = {32'h00_10_00_73}; + end else begin + // c.jalr -> jalr x1, rs1, 0 + instr_o = {12'b0, instr_i[11:7], 3'b000, 5'b00001, {OPCODE_JALR}}; + end + end + end + end + + 3'b110: begin + // c.swsp -> sw rs2, imm(x2) + instr_o = {4'b0, instr_i[8:7], instr_i[12], instr_i[6:2], 5'h02, 3'b010, + instr_i[11:9], 2'b00, {OPCODE_STORE}}; + end + + 3'b001, + 3'b011, + 3'b101, + 3'b111: begin + illegal_instr_o = 1'b1; + end + + // default: begin + // illegal_instr_o = 1'b1; + // end + endcase + end + + // Incoming instruction is not compressed. + 2'b11:; + + // default: begin + // illegal_instr_o = 1'b1; + // end + endcase + end + + assign is_compressed_o = (instr_i[1:0] != 2'b11); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_ifu_dummy_instr.sv b/verilog/rtl/brq_ifu_dummy_instr.sv new file mode 100644 index 0000000..91bec36 --- /dev/null +++ b/verilog/rtl/brq_ifu_dummy_instr.sv
@@ -0,0 +1,141 @@ + + +/** + * Dummy instruction module + * + * Provides pseudo-randomly inserted fake instructions for secure code obfuscation + */ + +module brq_ifu_dummy_instr ( + // Clock and reset + input logic clk_i, + input logic rst_ni, + + // Interface to CSRs + input logic dummy_instr_en_i, + input logic [2:0] dummy_instr_mask_i, + input logic dummy_instr_seed_en_i, + input logic [31:0] dummy_instr_seed_i, + + // Interface to IF stage + input logic fetch_valid_i, + input logic id_in_ready_i, + output logic insert_dummy_instr_o, + output logic [31:0] dummy_instr_data_o +); + + localparam int unsigned TIMEOUT_CNT_W = 5; + localparam int unsigned OP_W = 5; + + typedef enum logic [1:0] { + DUMMY_ADD = 2'b00, + DUMMY_MUL = 2'b01, + DUMMY_DIV = 2'b10, + DUMMY_AND = 2'b11 + } dummy_instr_e; + + typedef struct packed { + dummy_instr_e instr_type; + logic [OP_W-1:0] op_b; + logic [OP_W-1:0] op_a; + logic [TIMEOUT_CNT_W-1:0] cnt; + } lfsr_data_t; + localparam int unsigned LFSR_OUT_W = $bits(lfsr_data_t); + + lfsr_data_t lfsr_data; + logic [TIMEOUT_CNT_W-1:0] dummy_cnt_incr, dummy_cnt_threshold; + logic [TIMEOUT_CNT_W-1:0] dummy_cnt_d, dummy_cnt_q; + logic dummy_cnt_en; + logic lfsr_en; + logic [LFSR_OUT_W-1:0] lfsr_state; + logic insert_dummy_instr; + logic [6:0] dummy_set; + logic [2:0] dummy_opcode; + logic [31:0] dummy_instr; + logic [31:0] dummy_instr_seed_q, dummy_instr_seed_d; + + // Shift the LFSR every time we insert an instruction + assign lfsr_en = insert_dummy_instr & id_in_ready_i; + + assign dummy_instr_seed_d = dummy_instr_seed_q ^ dummy_instr_seed_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + dummy_instr_seed_q <= '0; + end else if (dummy_instr_seed_en_i) begin + dummy_instr_seed_q <= dummy_instr_seed_d; + end + end + + prim_lfsr #( + .LfsrDw ( 32 ), + .StateOutDw ( LFSR_OUT_W ) + ) lfsr_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .seed_en_i ( dummy_instr_seed_en_i ), + .seed_i ( dummy_instr_seed_d ), + .lfsr_en_i ( lfsr_en ), + .entropy_i ( '0 ), + .state_o ( lfsr_state ) + ); + + // Extract fields from LFSR + assign lfsr_data = lfsr_data_t'(lfsr_state); + + // Set count threshold for inserting a new instruction. This is the pseudo-random value from the + // LFSR with a mask applied (based on CSR config data) to shorten the period if required. + assign dummy_cnt_threshold = lfsr_data.cnt & {dummy_instr_mask_i,{TIMEOUT_CNT_W-3{1'b1}}}; + assign dummy_cnt_incr = dummy_cnt_q + {{TIMEOUT_CNT_W-1{1'b0}},1'b1}; + // Clear the counter everytime a new instruction is inserted + assign dummy_cnt_d = insert_dummy_instr ? '0 : dummy_cnt_incr; + // Increment the counter for each executed instruction while dummy instuctions are + // enabled. + assign dummy_cnt_en = dummy_instr_en_i & id_in_ready_i & + (fetch_valid_i | insert_dummy_instr); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + dummy_cnt_q <= '0; + end else if (dummy_cnt_en) begin + dummy_cnt_q <= dummy_cnt_d; + end + end + + // Insert a dummy instruction each time the counter hits the threshold + assign insert_dummy_instr = dummy_instr_en_i & (dummy_cnt_q == dummy_cnt_threshold); + + // Encode instruction + always_comb begin + unique case (lfsr_data.instr_type) + DUMMY_ADD : begin + dummy_set = 7'b0000000; + dummy_opcode = 3'b000; + end + DUMMY_MUL : begin + dummy_set = 7'b0000001; + dummy_opcode = 3'b000; + end + DUMMY_DIV : begin + dummy_set = 7'b0000001; + dummy_opcode = 3'b100; + end + DUMMY_AND : begin + dummy_set = 7'b0000000; + dummy_opcode = 3'b111; + end + default : begin + dummy_set = 7'b0000000; + dummy_opcode = 3'b000; + end + endcase + end + + // SET RS2 RS1 OP RD + assign dummy_instr = {dummy_set,lfsr_data.op_b,lfsr_data.op_a,dummy_opcode,5'h00,7'h33}; + + // Assign outputs + assign insert_dummy_instr_o = insert_dummy_instr; + assign dummy_instr_data_o = dummy_instr; + +endmodule
diff --git a/verilog/rtl/brq_ifu_fifo.sv b/verilog/rtl/brq_ifu_fifo.sv new file mode 100644 index 0000000..09bb06c --- /dev/null +++ b/verilog/rtl/brq_ifu_fifo.sv
@@ -0,0 +1,237 @@ + + +/** + * Fetch Fifo for 32 bit memory interface + * + * input port: send address and data to the FIFO + * clear_i clears the FIFO for the following cycle, including any new request + */ + + + +module brq_ifu_fifo #( + parameter int unsigned NUM_REQS = 2 +) ( + input logic clk_i, + input logic rst_ni, + + // control signals + input logic clear_i, // clears the contents of the FIFO + output logic [NUM_REQS-1:0] busy_o, + + // input port + input logic in_valid_i, + input logic [31:0] in_addr_i, + input logic [31:0] in_rdata_i, + input logic in_err_i, + + // output port + output logic out_valid_o, + input logic out_ready_i, + output logic [31:0] out_addr_o, + output logic [31:0] out_addr_next_o, + output logic [31:0] out_rdata_o, + output logic out_err_o, + output logic out_err_plus2_o +); + + localparam int unsigned DEPTH = NUM_REQS+1; + + // index 0 is used for output + logic [DEPTH-1:0] [31:0] rdata_d, rdata_q; + logic [DEPTH-1:0] err_d, err_q; + logic [DEPTH-1:0] valid_d, valid_q; + logic [DEPTH-1:0] lowest_free_entry; + logic [DEPTH-1:0] valid_pushed, valid_popped; + logic [DEPTH-1:0] entry_en; + + logic pop_fifo; + logic [31:0] rdata, rdata_unaligned; + logic err, err_unaligned, err_plus2; + logic valid, valid_unaligned; + + logic aligned_is_compressed, unaligned_is_compressed; + + logic addr_incr_two; + logic [31:1] instr_addr_next; + logic [31:1] instr_addr_d, instr_addr_q; + logic instr_addr_en; + logic unused_addr_in; + + ///////////////// + // Output port // + ///////////////// + + assign rdata = valid_q[0] ? rdata_q[0] : in_rdata_i; + assign err = valid_q[0] ? err_q[0] : in_err_i; + assign valid = valid_q[0] | in_valid_i; + + // The FIFO contains word aligned memory fetches, but the instructions contained in each entry + // might be half-word aligned (due to compressed instructions) + // e.g. + // | 31 16 | 15 0 | + // FIFO entry 0 | Instr 1 [15:0] | Instr 0 [15:0] | + // FIFO entry 1 | Instr 2 [15:0] | Instr 1 [31:16] | + // + // The FIFO also has a direct bypass path, so a complete instruction might be made up of data + // from the FIFO and new incoming data. + // + + // Construct the output data for an unaligned instruction + assign rdata_unaligned = valid_q[1] ? {rdata_q[1][15:0], rdata[31:16]} : + {in_rdata_i[15:0], rdata[31:16]}; + + // If entry[1] is valid, an error can come from entry[0] or entry[1], unless the + // instruction in entry[0] is compressed (entry[1] is a new instruction) + // If entry[1] is not valid, and entry[0] is, an error can come from entry[0] or the incoming + // data, unless the instruction in entry[0] is compressed + // If entry[0] is not valid, the error must come from the incoming data + assign err_unaligned = valid_q[1] ? ((err_q[1] & ~unaligned_is_compressed) | err_q[0]) : + ((valid_q[0] & err_q[0]) | + (in_err_i & (~valid_q[0] | ~unaligned_is_compressed))); + + // Record when an error is caused by the second half of an unaligned 32bit instruction. + // Only needs to be correct when unaligned and if err_unaligned is set + assign err_plus2 = valid_q[1] ? (err_q[1] & ~err_q[0]) : + (in_err_i & valid_q[0] & ~err_q[0]); + + // An uncompressed unaligned instruction is only valid if both parts are available + assign valid_unaligned = valid_q[1] ? 1'b1 : + (valid_q[0] & in_valid_i); + + // If there is an error, rdata is unknown + assign unaligned_is_compressed = (rdata[17:16] != 2'b11) & ~err; + assign aligned_is_compressed = (rdata[ 1: 0] != 2'b11) & ~err; + + //////////////////////////////////////// + // Instruction aligner (if unaligned) // + //////////////////////////////////////// + + always_comb begin + if (out_addr_o[1]) begin + // unaligned case + out_rdata_o = rdata_unaligned; + out_err_o = err_unaligned; + out_err_plus2_o = err_plus2; + + if (unaligned_is_compressed) begin + out_valid_o = valid; + end else begin + out_valid_o = valid_unaligned; + end + end else begin + // aligned case + out_rdata_o = rdata; + out_err_o = err; + out_err_plus2_o = 1'b0; + out_valid_o = valid; + end + end + + ///////////////////////// + // Instruction address // + ///////////////////////// + + // Update the address on branches and every time an instruction is driven + assign instr_addr_en = clear_i | (out_ready_i & out_valid_o); + + // Increment the address by two every time a compressed instruction is popped + assign addr_incr_two = instr_addr_q[1] ? unaligned_is_compressed : + aligned_is_compressed; + + assign instr_addr_next = (instr_addr_q[31:1] + + // Increment address by 4 or 2 + {29'd0,~addr_incr_two,addr_incr_two}); + + assign instr_addr_d = clear_i ? in_addr_i[31:1] : + instr_addr_next; + + always_ff @(posedge clk_i) begin + if (instr_addr_en) begin + instr_addr_q <= instr_addr_d; + end + end + + // Output both PC of current instruction and instruction following. PC of instruction following is + // required for the branch predictor. It's used to fetch the instruction following a branch that + // was not-taken but (mis)predicted taken. + assign out_addr_next_o = {instr_addr_next, 1'b0}; + assign out_addr_o = {instr_addr_q, 1'b0}; + + // The LSB of the address is unused, since all addresses are halfword aligned + assign unused_addr_in = in_addr_i[0]; + + ///////////////// + // FIFO status // + ///////////////// + + // Indicate the fill level of fifo-entries. This is used to determine when a new request can be + // made on the bus. The prefetch buffer only needs to know about the upper entries which overlap + // with NUM_REQS. + assign busy_o = valid_q[DEPTH-1:DEPTH-NUM_REQS]; + + ///////////////////// + // FIFO management // + ///////////////////// + + // Since an entry can contain unaligned instructions, popping an entry can leave the entry valid + assign pop_fifo = out_ready_i & out_valid_o & (~aligned_is_compressed | out_addr_o[1]); + + for (genvar i = 0; i < (DEPTH - 1); i++) begin : g_fifo_next + // Calculate lowest free entry (write pointer) + if (i == 0) begin : g_ent0 + assign lowest_free_entry[i] = ~valid_q[i]; + end else begin : g_ent_others + assign lowest_free_entry[i] = ~valid_q[i] & valid_q[i-1]; + end + + // An entry is set when an incoming request chooses the lowest available entry + assign valid_pushed[i] = (in_valid_i & lowest_free_entry[i]) | + valid_q[i]; + // Popping the FIFO shifts all entries down + assign valid_popped[i] = pop_fifo ? valid_pushed[i+1] : valid_pushed[i]; + // All entries are wiped out on a clear + assign valid_d[i] = valid_popped[i] & ~clear_i; + + // data flops are enabled if there is new data to shift into it, or + assign entry_en[i] = (valid_pushed[i+1] & pop_fifo) | + // a new request is incoming and this is the lowest free entry + (in_valid_i & lowest_free_entry[i] & ~pop_fifo); + + // take the next entry or the incoming data + assign rdata_d[i] = valid_q[i+1] ? rdata_q[i+1] : in_rdata_i; + assign err_d [i] = valid_q[i+1] ? err_q [i+1] : in_err_i; + end + // The top entry is similar but with simpler muxing + assign lowest_free_entry[DEPTH-1] = ~valid_q[DEPTH-1] & valid_q[DEPTH-2]; + assign valid_pushed [DEPTH-1] = valid_q[DEPTH-1] | (in_valid_i & lowest_free_entry[DEPTH-1]); + assign valid_popped [DEPTH-1] = pop_fifo ? 1'b0 : valid_pushed[DEPTH-1]; + assign valid_d [DEPTH-1] = valid_popped[DEPTH-1] & ~clear_i; + assign entry_en[DEPTH-1] = in_valid_i & lowest_free_entry[DEPTH-1]; + assign rdata_d [DEPTH-1] = in_rdata_i; + assign err_d [DEPTH-1] = in_err_i; + + //////////////////// + // FIFO registers // + //////////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + valid_q <= '0; + end else begin + valid_q <= valid_d; + end + end + + for (genvar i = 0; i < DEPTH; i++) begin : g_fifo_regs + always_ff @(posedge clk_i) begin + if (entry_en[i]) begin + rdata_q[i] <= rdata_d[i]; + err_q[i] <= err_d[i]; + end + end + end + + + +endmodule
diff --git a/verilog/rtl/brq_ifu_icache.sv b/verilog/rtl/brq_ifu_icache.sv new file mode 100644 index 0000000..51a998c --- /dev/null +++ b/verilog/rtl/brq_ifu_icache.sv
@@ -0,0 +1,1052 @@ + + +/** + * Instruction cache + * + * Provides an instruction cache along with cache management, instruction buffering and prefetching + */ + +module brq_ifu_icache #( + parameter bit BranchPredictor = 1'b0, + // Cache arrangement parameters + parameter int unsigned BusWidth = 32, + parameter int unsigned CacheSizeBytes = 4*1024, + parameter bit ICacheECC = 1'b0, + parameter int unsigned LineSize = 64, + parameter int unsigned NumWays = 2, + // Only cache branch targets + parameter bit BranchCache = 1'b0 +) ( + // Clock and reset + input logic clk_i, + input logic rst_ni, + + // Signal that the core would like instructions + input logic req_i, + + // Set the cache's address counter + input logic branch_i, + input logic branch_spec_i, + input logic predicted_branch_i, + input logic branch_mispredict_i, + input logic [31:0] addr_i, + + // IF stage interface: Pass fetched instructions to the core + input logic ready_i, + output logic valid_o, + output logic [31:0] rdata_o, + output logic [31:0] addr_o, + output logic err_o, + output logic err_plus2_o, + + // Instruction memory / interconnect interface: Fetch instruction data from memory + output logic instr_req_o, + input logic instr_gnt_i, + output logic [31:0] instr_addr_o, + input logic [BusWidth-1:0] instr_rdata_i, + input logic instr_err_i, + input logic instr_pmp_err_i, + input logic instr_rvalid_i, + + // Cache status + input logic icache_enable_i, + input logic icache_inval_i, + output logic busy_o +); + // Local constants + localparam int unsigned ADDR_W = 32; + // Number of fill buffers (must be >= 2) + localparam int unsigned NUM_FB = 4; + // Request throttling threshold + localparam int unsigned FB_THRESHOLD = NUM_FB - 2; + // Derived parameters + localparam int unsigned LINE_SIZE_ECC = ICacheECC ? (LineSize + 8) : LineSize; + localparam int unsigned LINE_SIZE_BYTES = LineSize/8; + localparam int unsigned LINE_W = $clog2(LINE_SIZE_BYTES); + localparam int unsigned BUS_BYTES = BusWidth/8; + localparam int unsigned BUS_W = $clog2(BUS_BYTES); + localparam int unsigned LINE_BEATS = LINE_SIZE_BYTES / BUS_BYTES; + localparam int unsigned LINE_BEATS_W = $clog2(LINE_BEATS); + localparam int unsigned NUM_LINES = CacheSizeBytes / NumWays / LINE_SIZE_BYTES; + localparam int unsigned INDEX_W = $clog2(NUM_LINES); + localparam int unsigned INDEX_HI = INDEX_W + LINE_W - 1; + localparam int unsigned TAG_SIZE = ADDR_W - INDEX_W - LINE_W + 1; // 1 valid bit + localparam int unsigned TAG_SIZE_ECC = ICacheECC ? (TAG_SIZE + 6) : TAG_SIZE; + localparam int unsigned OUTPUT_BEATS = (BUS_BYTES / 2); // number of halfwords + + // Prefetch signals + logic [ADDR_W-1:0] lookup_addr_aligned; + logic [ADDR_W-1:0] branch_mispredict_addr; + logic [ADDR_W-1:0] prefetch_addr_d, prefetch_addr_q; + logic prefetch_addr_en; + logic branch_or_mispredict; + // Cache pipelipe IC0 signals + logic branch_suppress; + logic lookup_throttle; + logic lookup_req_ic0; + logic [ADDR_W-1:0] lookup_addr_ic0; + logic [INDEX_W-1:0] lookup_index_ic0; + logic fill_req_ic0; + logic [INDEX_W-1:0] fill_index_ic0; + logic [TAG_SIZE-1:0] fill_tag_ic0; + logic [LineSize-1:0] fill_wdata_ic0; + logic lookup_grant_ic0; + logic lookup_actual_ic0; + logic fill_grant_ic0; + logic tag_req_ic0; + logic [INDEX_W-1:0] tag_index_ic0; + logic [NumWays-1:0] tag_banks_ic0; + logic tag_write_ic0; + logic [TAG_SIZE_ECC-1:0] tag_wdata_ic0; + logic data_req_ic0; + logic [INDEX_W-1:0] data_index_ic0; + logic [NumWays-1:0] data_banks_ic0; + logic data_write_ic0; + logic [LINE_SIZE_ECC-1:0] data_wdata_ic0; + // Cache pipelipe IC1 signals + logic [TAG_SIZE_ECC-1:0] tag_rdata_ic1 [NumWays]; + logic [LINE_SIZE_ECC-1:0] data_rdata_ic1 [NumWays]; + logic [LINE_SIZE_ECC-1:0] hit_data_ic1; + logic lookup_valid_ic1; + logic [ADDR_W-1:INDEX_HI+1] lookup_addr_ic1; + logic [NumWays-1:0] tag_match_ic1; + logic tag_hit_ic1; + logic [NumWays-1:0] tag_invalid_ic1; + logic [NumWays-1:0] lowest_invalid_way_ic1; + logic [NumWays-1:0] round_robin_way_ic1, round_robin_way_q; + logic [NumWays-1:0] sel_way_ic1; + logic ecc_err_ic1; + logic ecc_write_req; + logic [NumWays-1:0] ecc_write_ways; + logic [INDEX_W-1:0] ecc_write_index; + // Fill buffer signals + logic gnt_or_pmp_err, gnt_not_pmp_err; + logic [$clog2(NUM_FB)-1:0] fb_fill_level; + logic fill_cache_new; + logic fill_new_alloc; + logic fill_spec_req, fill_spec_done, fill_spec_hold; + logic [NUM_FB-1:0][NUM_FB-1:0] fill_older_d, fill_older_q; + logic [NUM_FB-1:0] fill_alloc_sel, fill_alloc; + logic [NUM_FB-1:0] fill_busy_d, fill_busy_q; + logic [NUM_FB-1:0] fill_done; + logic [NUM_FB-1:0] fill_in_ic1; + logic [NUM_FB-1:0] fill_stale_d, fill_stale_q; + logic [NUM_FB-1:0] fill_cache_d, fill_cache_q; + logic [NUM_FB-1:0] fill_hit_ic1, fill_hit_d, fill_hit_q; + logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_ext_cnt_d, fill_ext_cnt_q; + logic [NUM_FB-1:0] fill_ext_hold_d, fill_ext_hold_q; + logic [NUM_FB-1:0] fill_ext_done_d, fill_ext_done_q; + logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_rvd_cnt_d, fill_rvd_cnt_q; + logic [NUM_FB-1:0] fill_rvd_done; + logic [NUM_FB-1:0] fill_ram_done_d, fill_ram_done_q; + logic [NUM_FB-1:0] fill_out_grant; + logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_out_cnt_d, fill_out_cnt_q; + logic [NUM_FB-1:0] fill_out_done; + logic [NUM_FB-1:0] fill_ext_req, fill_rvd_exp, fill_ram_req, fill_out_req; + logic [NUM_FB-1:0] fill_data_sel, fill_data_reg, fill_data_hit, fill_data_rvd; + logic [NUM_FB-1:0][LINE_BEATS_W-1:0] fill_ext_off, fill_rvd_off; + logic [NUM_FB-1:0][LINE_BEATS_W:0] fill_ext_beat, fill_rvd_beat; + logic [NUM_FB-1:0] fill_ext_arb, fill_ram_arb, fill_out_arb; + logic [NUM_FB-1:0] fill_rvd_arb; + logic [NUM_FB-1:0] fill_entry_en; + logic [NUM_FB-1:0] fill_addr_en; + logic [NUM_FB-1:0] fill_way_en; + logic [NUM_FB-1:0][LINE_BEATS-1:0] fill_data_en; + logic [NUM_FB-1:0][LINE_BEATS-1:0] fill_err_d, fill_err_q; + logic [ADDR_W-1:0] fill_addr_q [NUM_FB]; + logic [NumWays-1:0] fill_way_q [NUM_FB]; + logic [LineSize-1:0] fill_data_d [NUM_FB]; + logic [LineSize-1:0] fill_data_q [NUM_FB]; + logic [ADDR_W-1:BUS_W] fill_ext_req_addr; + logic [ADDR_W-1:0] fill_ram_req_addr; + logic [NumWays-1:0] fill_ram_req_way; + logic [LineSize-1:0] fill_ram_req_data; + logic [LineSize-1:0] fill_out_data; + logic [LINE_BEATS-1:0] fill_out_err; + // External req signals + logic instr_req; + logic [ADDR_W-1:BUS_W] instr_addr; + // Data output signals + logic skid_complete_instr; + logic skid_ready; + logic output_compressed; + logic skid_valid_d, skid_valid_q, skid_en; + logic [15:0] skid_data_d, skid_data_q; + logic skid_err_q; + logic output_valid; + logic addr_incr_two; + logic output_addr_en; + logic [ADDR_W-1:1] output_addr_incr; + logic [ADDR_W-1:1] output_addr_d, output_addr_q; + logic [15:0] output_data_lo, output_data_hi; + logic data_valid, output_ready; + logic [LineSize-1:0] line_data; + logic [LINE_BEATS-1:0] line_err; + logic [31:0] line_data_muxed; + logic line_err_muxed; + logic [31:0] output_data; + logic output_err; + // Invalidations + logic start_inval, inval_done; + logic reset_inval_q; + logic inval_prog_d, inval_prog_q; + logic [INDEX_W-1:0] inval_index_d, inval_index_q; + + ////////////////////////// + // Instruction prefetch // + ////////////////////////// + + if (BranchPredictor) begin : g_branch_predictor + // Where the branch predictor is present record what address followed a predicted branch. If + // that branch is predicted taken but mispredicted (so not-taken) this is used to resume on + // the not-taken code path. + logic [31:0] branch_mispredict_addr_q; + logic branch_mispredict_addr_en; + + assign branch_mispredict_addr_en = branch_i & predicted_branch_i; + + always_ff @(posedge clk_i) begin + if (branch_mispredict_addr_en) begin + branch_mispredict_addr_q <= {output_addr_incr, 1'b0}; + end + end + + assign branch_mispredict_addr = branch_mispredict_addr_q; + + end else begin : g_no_branch_predictor + logic unused_predicted_branch; + + assign unused_predicted_branch = predicted_branch_i; + + assign branch_mispredict_addr = '0; + end + + assign branch_or_mispredict = branch_i | branch_mispredict_i; + + assign lookup_addr_aligned = {lookup_addr_ic0[ADDR_W-1:LINE_W],{LINE_W{1'b0}}}; + + // The prefetch address increments by one cache line for each granted request. + // This address is also updated if there is a branch that is not granted, since the target + // address (addr_i) is only valid for one cycle while branch_i is high. + + // The captured branch target address is not forced to be aligned since the offset in the cache + // line must also be recorded for later use by the fill buffers. + assign prefetch_addr_d = + lookup_grant_ic0 ? (lookup_addr_aligned + {{ADDR_W-LINE_W-1{1'b0}},1'b1,{LINE_W{1'b0}}}) : + branch_i ? addr_i : + branch_mispredict_addr; + + assign prefetch_addr_en = branch_or_mispredict | lookup_grant_ic0; + + always_ff @(posedge clk_i) begin + if (prefetch_addr_en) begin + prefetch_addr_q <= prefetch_addr_d; + end + end + + //////////////////////// + // Pipeline stage IC0 // + //////////////////////// + + // Cache lookup + assign lookup_throttle = (fb_fill_level > FB_THRESHOLD[$clog2(NUM_FB)-1:0]); + + assign lookup_req_ic0 = req_i & ~&fill_busy_q & (branch_or_mispredict | ~lookup_throttle) & + ~ecc_write_req; + assign lookup_addr_ic0 = branch_spec_i ? addr_i : + branch_mispredict_i ? branch_mispredict_addr : + prefetch_addr_q; + assign lookup_index_ic0 = lookup_addr_ic0[INDEX_HI:LINE_W]; + + // Cache write + assign fill_req_ic0 = (|fill_ram_req); + assign fill_index_ic0 = fill_ram_req_addr[INDEX_HI:LINE_W]; + assign fill_tag_ic0 = {(~inval_prog_q & ~ecc_write_req),fill_ram_req_addr[ADDR_W-1:INDEX_HI+1]}; + assign fill_wdata_ic0 = fill_ram_req_data; + + // Suppress a new lookup on a not-taken branch (as the address will be incorrect) + assign branch_suppress = branch_spec_i & ~branch_i; + + // Arbitrated signals - lookups have highest priority + assign lookup_grant_ic0 = lookup_req_ic0 & ~branch_suppress; + assign fill_grant_ic0 = fill_req_ic0 & (~lookup_req_ic0 | branch_suppress) & ~inval_prog_q & + ~ecc_write_req; + // Qualified lookup grant to mask ram signals in IC1 if access was not made + assign lookup_actual_ic0 = lookup_grant_ic0 & icache_enable_i & ~inval_prog_q & ~start_inval; + + // Tagram + assign tag_req_ic0 = lookup_req_ic0 | fill_req_ic0 | inval_prog_q | ecc_write_req; + assign tag_index_ic0 = inval_prog_q ? inval_index_q : + ecc_write_req ? ecc_write_index : + fill_grant_ic0 ? fill_index_ic0 : + lookup_index_ic0; + assign tag_banks_ic0 = ecc_write_req ? ecc_write_ways : + fill_grant_ic0 ? fill_ram_req_way : + {NumWays{1'b1}}; + assign tag_write_ic0 = fill_grant_ic0 | inval_prog_q | ecc_write_req; + + // Dataram + assign data_req_ic0 = lookup_req_ic0 | fill_req_ic0; + assign data_index_ic0 = tag_index_ic0; + assign data_banks_ic0 = tag_banks_ic0; + assign data_write_ic0 = tag_write_ic0; + + // Append ECC checkbits to write data if required + if (ICacheECC) begin : gen_ecc_wdata + + // Tagram ECC + // Reuse the same ecc encoding module for larger cache sizes by padding with zeros + logic [21:0] tag_ecc_input_padded; + logic [27:0] tag_ecc_output_padded; + logic [22-TAG_SIZE:0] tag_ecc_output_unused; + + assign tag_ecc_input_padded = {{22-TAG_SIZE{1'b0}},fill_tag_ic0}; + assign tag_ecc_output_unused = tag_ecc_output_padded[21:TAG_SIZE-1]; + + prim_secded_28_22_enc tag_ecc_enc ( + .in (tag_ecc_input_padded), + .out (tag_ecc_output_padded) + ); + + assign tag_wdata_ic0 = {tag_ecc_output_padded[27:22],tag_ecc_output_padded[TAG_SIZE-1:0]}; + + // Dataram ECC + prim_secded_72_64_enc data_ecc_enc ( + .in (fill_wdata_ic0), + .out (data_wdata_ic0) + ); + + end else begin : gen_noecc_wdata + assign tag_wdata_ic0 = fill_tag_ic0; + assign data_wdata_ic0 = fill_wdata_ic0; + end + + //////////////// + // IC0 -> IC1 // + //////////////// + + for (genvar way = 0; way < NumWays; way++) begin : gen_rams + // Tag RAM instantiation + prim_ram_1p #( + .Width (TAG_SIZE_ECC), + .Depth (NUM_LINES), + .DataBitsPerMask (TAG_SIZE_ECC) + ) tag_bank ( + .clk_i (clk_i), + .req_i (tag_req_ic0 & tag_banks_ic0[way]), + .write_i (tag_write_ic0), + .wmask_i ({TAG_SIZE_ECC{1'b1}}), + .addr_i (tag_index_ic0), + .wdata_i (tag_wdata_ic0), + .rdata_o (tag_rdata_ic1[way]) + ); + // Data RAM instantiation + prim_ram_1p #( + .Width (LINE_SIZE_ECC), + .Depth (NUM_LINES), + .DataBitsPerMask (LINE_SIZE_ECC) + ) data_bank ( + .clk_i (clk_i), + .req_i (data_req_ic0 & data_banks_ic0[way]), + .write_i (data_write_ic0), + .wmask_i ({LINE_SIZE_ECC{1'b1}}), + .addr_i (data_index_ic0), + .wdata_i (data_wdata_ic0), + .rdata_o (data_rdata_ic1[way]) + ); + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + lookup_valid_ic1 <= 1'b0; + end else begin + lookup_valid_ic1 <= lookup_actual_ic0; + end + end + + always_ff @(posedge clk_i) begin + if (lookup_grant_ic0) begin + lookup_addr_ic1 <= lookup_addr_ic0[ADDR_W-1:INDEX_HI+1]; + fill_in_ic1 <= fill_alloc_sel; + end + end + + //////////////////////// + // Pipeline stage IC1 // + //////////////////////// + + // Tag matching + for (genvar way = 0; way < NumWays; way++) begin : gen_tag_match + assign tag_match_ic1[way] = (tag_rdata_ic1[way][TAG_SIZE-1:0] == + {1'b1,lookup_addr_ic1[ADDR_W-1:INDEX_HI+1]}); + assign tag_invalid_ic1[way] = ~tag_rdata_ic1[way][TAG_SIZE-1]; + end + + assign tag_hit_ic1 = |tag_match_ic1; + + // Hit data mux + always_comb begin + hit_data_ic1 = 'b0; + for (int way = 0; way < NumWays; way++) begin + if (tag_match_ic1[way]) begin + hit_data_ic1 |= data_rdata_ic1[way]; + end + end + end + + // Way selection for allocations to the cache (onehot signals) + // 1 first invalid way + // 2 global round-robin (pseudorandom) way + assign lowest_invalid_way_ic1[0] = tag_invalid_ic1[0]; + assign round_robin_way_ic1[0] = round_robin_way_q[NumWays-1]; + for (genvar way = 1; way < NumWays; way++) begin : gen_lowest_way + assign lowest_invalid_way_ic1[way] = tag_invalid_ic1[way] & ~|tag_invalid_ic1[way-1:0]; + assign round_robin_way_ic1[way] = round_robin_way_q[way-1]; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + round_robin_way_q <= {{NumWays-1{1'b0}},1'b1}; + end else if (lookup_valid_ic1) begin + round_robin_way_q <= round_robin_way_ic1; + end + end + + assign sel_way_ic1 = |tag_invalid_ic1 ? lowest_invalid_way_ic1 : + round_robin_way_q; + + // ECC checking logic + if (ICacheECC) begin : gen_data_ecc_checking + logic [NumWays-1:0] tag_err_ic1; + logic [1:0] data_err_ic1; + logic ecc_correction_write_d, ecc_correction_write_q; + logic [NumWays-1:0] ecc_correction_ways_d, ecc_correction_ways_q; + logic [INDEX_W-1:0] lookup_index_ic1, ecc_correction_index_q; + + // Tag ECC checking + for (genvar way = 0; way < NumWays; way++) begin : gen_tag_ecc + logic [1:0] tag_err_bank_ic1; + logic [27:0] tag_rdata_padded_ic1; + + // Expand the tag rdata with extra padding if the tag size is less than the maximum + assign tag_rdata_padded_ic1 = {tag_rdata_ic1[way][TAG_SIZE_ECC-1-:6], + {22-TAG_SIZE{1'b0}}, + tag_rdata_ic1[way][TAG_SIZE-1:0]}; + + prim_secded_28_22_dec data_ecc_dec ( + .in (tag_rdata_padded_ic1), + .d_o (), + .syndrome_o (), + .err_o (tag_err_bank_ic1) + ); + assign tag_err_ic1[way] = |tag_err_bank_ic1; + end + + // Data ECC checking + // Note - could generate for all ways and mux after + prim_secded_72_64_dec data_ecc_dec ( + .in (hit_data_ic1), + .d_o (), + .syndrome_o (), + .err_o (data_err_ic1) + ); + + assign ecc_err_ic1 = lookup_valid_ic1 & ((|data_err_ic1) | (|tag_err_ic1)); + + // Error correction + // All ways will be invalidated on a tag error to prevent X-propagation from data_err_ic1 on + // spurious hits. Also prevents the same line being allocated twice when there was a true + // hit and a spurious hit. + assign ecc_correction_ways_d = {NumWays{|tag_err_ic1}} | + (tag_match_ic1 & {NumWays{|data_err_ic1}}); + assign ecc_correction_write_d = ecc_err_ic1; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ecc_correction_write_q <= 1'b0; + end else begin + ecc_correction_write_q <= ecc_correction_write_d; + end + end + + // The index is required in IC1 only when ECC is configured so is registered here + always_ff @(posedge clk_i) begin + if (lookup_grant_ic0) begin + lookup_index_ic1 <= lookup_addr_ic0[INDEX_HI-:INDEX_W]; + end + end + + // Store the ways with errors to be invalidated + always_ff @(posedge clk_i) begin + if (ecc_err_ic1) begin + ecc_correction_ways_q <= ecc_correction_ways_d; + ecc_correction_index_q <= lookup_index_ic1; + end + end + + assign ecc_write_req = ecc_correction_write_q; + assign ecc_write_ways = ecc_correction_ways_q; + assign ecc_write_index = ecc_correction_index_q; + + end else begin : gen_no_data_ecc + assign ecc_err_ic1 = 1'b0; + assign ecc_write_req = 1'b0; + assign ecc_write_ways = '0; + assign ecc_write_index = '0; + end + + /////////////////////////////// + // Cache allocation decision // + /////////////////////////////// + + if (BranchCache) begin : gen_caching_logic + + // Cache branch target + a number of subsequent lines + localparam int unsigned CACHE_AHEAD = 2; + localparam int unsigned CACHE_CNT_W = (CACHE_AHEAD == 1) ? 1 : $clog2(CACHE_AHEAD) + 1; + logic cache_cnt_dec; + logic [CACHE_CNT_W-1:0] cache_cnt_d, cache_cnt_q; + + assign cache_cnt_dec = lookup_grant_ic0 & (|cache_cnt_q); + assign cache_cnt_d = branch_i ? CACHE_AHEAD[CACHE_CNT_W-1:0] : + (cache_cnt_q - {{CACHE_CNT_W-1{1'b0}},cache_cnt_dec}); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cache_cnt_q <= '0; + end else begin + cache_cnt_q <= cache_cnt_d; + end + end + + assign fill_cache_new = (branch_i | (|cache_cnt_q)) & icache_enable_i & + ~icache_inval_i & ~inval_prog_q; + + end else begin : gen_cache_all + + // Cache all missing fetches + assign fill_cache_new = icache_enable_i & ~start_inval & ~inval_prog_q; + end + + ////////////////////////// + // Fill buffer tracking // + ////////////////////////// + + always_comb begin + fb_fill_level = '0; + for (int i = 0; i < NUM_FB; i++) begin + if (fill_busy_q[i] & ~fill_stale_q[i]) begin + fb_fill_level += {{$clog2(NUM_FB)-1{1'b0}},1'b1}; + end + end + end + + // PMP errors might not / don't need to be granted (since the external request is masked) + assign gnt_or_pmp_err = instr_gnt_i | instr_pmp_err_i; + assign gnt_not_pmp_err = instr_gnt_i & ~instr_pmp_err_i; + // Allocate a new buffer for every granted lookup + assign fill_new_alloc = lookup_grant_ic0; + // Track whether a speculative external request was made from IC0, and whether it was granted + // Speculative requests are only made for branches, or if the cache is disabled + assign fill_spec_req = (~icache_enable_i | branch_or_mispredict) & ~|fill_ext_req; + assign fill_spec_done = fill_spec_req & gnt_not_pmp_err; + assign fill_spec_hold = fill_spec_req & ~gnt_or_pmp_err; + + for (genvar fb = 0; fb < NUM_FB; fb++) begin : gen_fbs + + ///////////////////////////// + // Fill buffer allocations // + ///////////////////////////// + + // Allocate the lowest available buffer + if (fb == 0) begin : gen_fb_zero + assign fill_alloc_sel[fb] = ~fill_busy_q[fb]; + end else begin : gen_fb_rest + assign fill_alloc_sel[fb] = ~fill_busy_q[fb] & (&fill_busy_q[fb-1:0]); + end + + assign fill_alloc[fb] = fill_alloc_sel[fb] & fill_new_alloc; + assign fill_busy_d[fb] = fill_alloc[fb] | (fill_busy_q[fb] & ~fill_done[fb]); + + // Track which other fill buffers are older than this one (for age-based arbitration) + // TODO sparsify + assign fill_older_d[fb] = (fill_alloc[fb] ? fill_busy_q : fill_older_q[fb]) & ~fill_done; + + // A fill buffer can release once all its actions are completed + // all data written to the cache (unless hit or error) + assign fill_done[fb] = (fill_ram_done_q[fb] | fill_hit_q[fb] | ~fill_cache_q[fb] | + (|fill_err_q[fb])) & + // all data output unless stale due to intervening branch + (fill_out_done[fb] | fill_stale_q[fb] | branch_or_mispredict) & + // all external requests completed + fill_rvd_done[fb]; + + ///////////////////////////////// + // Fill buffer status tracking // + ///////////////////////////////// + + // Track staleness (requests become stale when a branch intervenes) + assign fill_stale_d[fb] = fill_busy_q[fb] & (branch_or_mispredict | fill_stale_q[fb]); + // Track whether or not this request should allocate to the cache + // Any invalidation or disabling of the cache while the buffer is busy will stop allocation + assign fill_cache_d[fb] = (fill_alloc[fb] & fill_cache_new) | + (fill_cache_q[fb] & fill_busy_q[fb] & + icache_enable_i & ~icache_inval_i); + // Record whether the request hit in the cache + assign fill_hit_ic1[fb] = lookup_valid_ic1 & fill_in_ic1[fb] & tag_hit_ic1 & ~ecc_err_ic1; + assign fill_hit_d[fb] = fill_hit_ic1[fb] | (fill_hit_q[fb] & fill_busy_q[fb]); + + /////////////////////////////////////////// + // Fill buffer external request tracking // + /////////////////////////////////////////// + + // Make an external request + assign fill_ext_req[fb] = fill_busy_q[fb] & ~fill_ext_done_d[fb]; + + // Count the number of completed external requests (each line requires LINE_BEATS requests) + // Don't count fake PMP error grants here since they will never receive an rvalid response + assign fill_ext_cnt_d[fb] = fill_alloc[fb] ? + {{LINE_BEATS_W{1'b0}},fill_spec_done} : + (fill_ext_cnt_q[fb] + {{LINE_BEATS_W{1'b0}}, + fill_ext_arb[fb] & gnt_not_pmp_err}); + // External request must be held until granted + assign fill_ext_hold_d[fb] = (fill_alloc[fb] & fill_spec_hold) | + (fill_ext_arb[fb] & ~gnt_or_pmp_err); + // External requests are completed when the counter is filled or when the request is cancelled + assign fill_ext_done_d[fb] = (fill_ext_cnt_q[fb][LINE_BEATS_W] | + // external requests are considered complete if the request hit + fill_hit_ic1[fb] | fill_hit_q[fb] | + // external requests will stop once any PMP error is received + fill_err_q[fb][fill_ext_off[fb]] | + // cancel if the line won't be cached and, it is stale + (~fill_cache_q[fb] & (branch_or_mispredict | fill_stale_q[fb] | + // or we're already at the end of the line + fill_ext_beat[fb][LINE_BEATS_W]))) & + // can't cancel while we are waiting for a grant on the bus + ~fill_ext_hold_q[fb] & fill_busy_q[fb]; + // Track whether this fill buffer expects to receive beats of data + assign fill_rvd_exp[fb] = fill_busy_q[fb] & ~fill_rvd_done[fb]; + // Count the number of rvalid beats received + assign fill_rvd_cnt_d[fb] = fill_alloc[fb] ? '0 : + (fill_rvd_cnt_q[fb] + + {{LINE_BEATS_W{1'b0}},fill_rvd_arb[fb]}); + // External data is complete when all issued external requests have received their data + assign fill_rvd_done[fb] = (fill_ext_done_q[fb] & ~fill_ext_hold_q[fb]) & + (fill_rvd_cnt_q[fb] == fill_ext_cnt_q[fb]); + + ////////////////////////////////////// + // Fill buffer data output tracking // + ////////////////////////////////////// + + // Send data to the IF stage for requests that are not stale, have not completed their + // data output, and have data available to send. + // Data is available if: + // - The request hit in the cache + // - The current beat is an error (since a PMP error might not actually receive any data) + // - Buffered data is available (fill_rvd_cnt_q is ahead of fill_out_cnt_q) + // - Data is available from the bus this cycle (fill_rvd_arb) + assign fill_out_req[fb] = fill_busy_q[fb] & ~fill_stale_q[fb] & ~fill_out_done[fb] & + (fill_hit_ic1[fb] | fill_hit_q[fb] | + (fill_err_q[fb][fill_out_cnt_q[fb][LINE_BEATS_W-1:0]]) | + (fill_rvd_beat[fb] > fill_out_cnt_q[fb]) | fill_rvd_arb[fb]); + + // Calculate when a beat of data is output. Any ECC error squashes the output that cycle. + assign fill_out_grant[fb] = fill_out_arb[fb] & output_ready; + + // Count the beats of data output to the IF stage + assign fill_out_cnt_d[fb] = fill_alloc[fb] ? {1'b0,lookup_addr_ic0[LINE_W-1:BUS_W]} : + (fill_out_cnt_q[fb] + + {{LINE_BEATS_W{1'b0}},fill_out_grant[fb]}); + // Data output complete when the counter fills + assign fill_out_done[fb] = fill_out_cnt_q[fb][LINE_BEATS_W]; + + ////////////////////////////////////// + // Fill buffer ram request tracking // + ////////////////////////////////////// + + // make a fill request once all data beats received + assign fill_ram_req[fb] = fill_busy_q[fb] & fill_rvd_cnt_q[fb][LINE_BEATS_W] & + // unless the request hit, was non-allocating or got an error + ~fill_hit_q[fb] & fill_cache_q[fb] & ~|fill_err_q[fb] & + // or the request was already completed + ~fill_ram_done_q[fb]; + + // Record when a cache allocation request has been completed + assign fill_ram_done_d[fb] = fill_ram_arb[fb] | (fill_ram_done_q[fb] & fill_busy_q[fb]); + + ////////////////////////////// + // Fill buffer line offsets // + ////////////////////////////// + + // When we branch into the middle of a line, the output count will not start from zero. This + // beat count is used to know which incoming rdata beats are relevant. + assign fill_ext_beat[fb] = {1'b0,fill_addr_q[fb][LINE_W-1:BUS_W]} + + fill_ext_cnt_q[fb][LINE_BEATS_W:0]; + assign fill_ext_off[fb] = fill_ext_beat[fb][LINE_BEATS_W-1:0]; + assign fill_rvd_beat[fb] = {1'b0,fill_addr_q[fb][LINE_W-1:BUS_W]} + + fill_rvd_cnt_q[fb][LINE_BEATS_W:0]; + assign fill_rvd_off[fb] = fill_rvd_beat[fb][LINE_BEATS_W-1:0]; + + ///////////////////////////// + // Fill buffer arbitration // + ///////////////////////////// + + // Age based arbitration - all these signals are one-hot + assign fill_ext_arb[fb] = fill_ext_req[fb] & ~|(fill_ext_req & fill_older_q[fb]); + assign fill_ram_arb[fb] = fill_ram_req[fb] & fill_grant_ic0 & ~|(fill_ram_req & fill_older_q[fb]); + // Calculate which fill buffer is the oldest one which still needs to output data to IF + assign fill_data_sel[fb] = ~|(fill_busy_q & ~fill_out_done & ~fill_stale_q & + fill_older_q[fb]); + // Arbitrate the request which has data available to send, and is the oldest outstanding + assign fill_out_arb[fb] = fill_out_req[fb] & fill_data_sel[fb]; + // Assign incoming rvalid data to the oldest fill buffer expecting it + assign fill_rvd_arb[fb] = instr_rvalid_i & fill_rvd_exp[fb] & ~|(fill_rvd_exp & fill_older_q[fb]); + + ///////////////////////////// + // Fill buffer data muxing // + ///////////////////////////// + + // Output data muxing controls + // 1. Select data from the fill buffer data register + assign fill_data_reg[fb] = fill_busy_q[fb] & ~fill_stale_q[fb] & + ~fill_out_done[fb] & fill_data_sel[fb] & + // The incoming data is already ahead of the output count + ((fill_rvd_beat[fb] > fill_out_cnt_q[fb]) | fill_hit_q[fb] | + (|fill_err_q[fb])); + // 2. Select IC1 hit data + assign fill_data_hit[fb] = fill_busy_q[fb] & fill_hit_ic1[fb] & fill_data_sel[fb]; + // 3. Select incoming instr_rdata_i + assign fill_data_rvd[fb] = fill_busy_q[fb] & fill_rvd_arb[fb] & ~fill_hit_q[fb] & + ~fill_hit_ic1[fb] & ~fill_stale_q[fb] & ~fill_out_done[fb] & + // The incoming data lines up with the output count + (fill_rvd_beat[fb] == fill_out_cnt_q[fb]) & fill_data_sel[fb]; + + + /////////////////////////// + // Fill buffer registers // + /////////////////////////// + + // Fill buffer general enable + assign fill_entry_en[fb] = fill_alloc[fb] | fill_busy_q[fb]; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fill_busy_q[fb] <= 1'b0; + fill_older_q[fb] <= '0; + fill_stale_q[fb] <= 1'b0; + fill_cache_q[fb] <= 1'b0; + fill_hit_q[fb] <= 1'b0; + fill_ext_cnt_q[fb] <= '0; + fill_ext_hold_q[fb] <= 1'b0; + fill_ext_done_q[fb] <= 1'b0; + fill_rvd_cnt_q[fb] <= '0; + fill_ram_done_q[fb] <= 1'b0; + fill_out_cnt_q[fb] <= '0; + end else if (fill_entry_en[fb]) begin + fill_busy_q[fb] <= fill_busy_d[fb]; + fill_older_q[fb] <= fill_older_d[fb]; + fill_stale_q[fb] <= fill_stale_d[fb]; + fill_cache_q[fb] <= fill_cache_d[fb]; + fill_hit_q[fb] <= fill_hit_d[fb]; + fill_ext_cnt_q[fb] <= fill_ext_cnt_d[fb]; + fill_ext_hold_q[fb] <= fill_ext_hold_d[fb]; + fill_ext_done_q[fb] <= fill_ext_done_d[fb]; + fill_rvd_cnt_q[fb] <= fill_rvd_cnt_d[fb]; + fill_ram_done_q[fb] <= fill_ram_done_d[fb]; + fill_out_cnt_q[fb] <= fill_out_cnt_d[fb]; + end + end + + //////////////////////////////////////// + // Fill buffer address / data storage // + //////////////////////////////////////// + + assign fill_addr_en[fb] = fill_alloc[fb]; + assign fill_way_en[fb] = (lookup_valid_ic1 & fill_in_ic1[fb]); + + always_ff @(posedge clk_i) begin + if (fill_addr_en[fb]) begin + fill_addr_q[fb] <= lookup_addr_ic0; + end + end + + always_ff @(posedge clk_i) begin + if (fill_way_en[fb]) begin + fill_way_q[fb] <= sel_way_ic1; + end + end + + // Data either comes from the cache or the bus. If there was an ECC error, we must take + // the incoming bus data since the cache hit data is corrupted. + assign fill_data_d[fb] = fill_hit_ic1[fb] ? hit_data_ic1[LineSize-1:0] : + {LINE_BEATS{instr_rdata_i}}; + + for (genvar b = 0; b < LINE_BEATS; b++) begin : gen_data_buf + // Error tracking (per beat) + // Either a PMP error on a speculative request, + assign fill_err_d[fb][b] = (instr_pmp_err_i & fill_alloc[fb] & fill_spec_req & + (lookup_addr_ic0[LINE_W-1:BUS_W] == b[LINE_BEATS_W-1:0])) | + // a PMP error on a fill buffer ext req + (instr_pmp_err_i & fill_ext_arb[fb] & + (fill_ext_off[fb] == b[LINE_BEATS_W-1:0])) | + // Or a data error with instr_rvalid_i + (fill_rvd_arb[fb] & instr_err_i & + (fill_rvd_off[fb] == b[LINE_BEATS_W-1:0])) | + // Hold the error once recorded + (fill_busy_q[fb] & fill_err_q[fb][b]); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fill_err_q[fb][b] <= '0; + end else if (fill_entry_en[fb]) begin + fill_err_q[fb][b] <= fill_err_d[fb][b]; + end + end + + // Enable the relevant part of the data register (or all for cache hits) + // Ignore incoming rvalid data when we already have cache hit data + assign fill_data_en[fb][b] = fill_hit_ic1[fb] | + (fill_rvd_arb[fb] & ~fill_hit_q[fb] & + (fill_rvd_off[fb] == b[LINE_BEATS_W-1:0])); + + always_ff @(posedge clk_i) begin + if (fill_data_en[fb][b]) begin + fill_data_q[fb][b*BusWidth+:BusWidth] <= fill_data_d[fb][b*BusWidth+:BusWidth]; + end + end + + end + end + + //////////////////////////////// + // Fill buffer one-hot muxing // + //////////////////////////////// + + // External req info + always_comb begin + fill_ext_req_addr = '0; + for (int i = 0; i < NUM_FB; i++) begin + if (fill_ext_arb[i]) begin + fill_ext_req_addr |= {fill_addr_q[i][ADDR_W-1:LINE_W], fill_ext_off[i]}; + end + end + end + + // Cache req info + always_comb begin + fill_ram_req_addr = '0; + fill_ram_req_way = '0; + fill_ram_req_data = '0; + for (int i = 0; i < NUM_FB; i++) begin + if (fill_ram_arb[i]) begin + fill_ram_req_addr |= fill_addr_q[i]; + fill_ram_req_way |= fill_way_q[i]; + fill_ram_req_data |= fill_data_q[i]; + end + end + end + + // IF stage output data + always_comb begin + fill_out_data = '0; + fill_out_err = '0; + for (int i = 0; i < NUM_FB; i++) begin + if (fill_data_reg[i]) begin + fill_out_data |= fill_data_q[i]; + // Ignore any speculative errors accumulated on cache hits + fill_out_err |= (fill_err_q[i] & ~{LINE_BEATS{fill_hit_q[i]}}); + end + end + end + + /////////////////////// + // External requests // + /////////////////////// + + assign instr_req = ((~icache_enable_i | branch_or_mispredict) & lookup_grant_ic0) | + (|fill_ext_req); + + assign instr_addr = |fill_ext_req ? fill_ext_req_addr : + lookup_addr_ic0[ADDR_W-1:BUS_W]; + + assign instr_req_o = instr_req; + assign instr_addr_o = {instr_addr[ADDR_W-1:BUS_W],{BUS_W{1'b0}}}; + + //////////////////////// + // Output data muxing // + //////////////////////// + + // Mux between line-width data sources + assign line_data = |fill_data_hit ? hit_data_ic1[LineSize-1:0] : fill_out_data; + assign line_err = |fill_data_hit ? {LINE_BEATS{1'b0}} : fill_out_err; + + // Mux the relevant beat of line data, based on the output address + always_comb begin + line_data_muxed = '0; + line_err_muxed = 1'b0; + for (int i = 0; i < LINE_BEATS; i++) begin + // When data has been skidded, the output address is behind by one + if ((output_addr_q[LINE_W-1:BUS_W] + {{LINE_BEATS_W-1{1'b0}},skid_valid_q}) == + i[LINE_BEATS_W-1:0]) begin + line_data_muxed |= line_data[i*32+:32]; + line_err_muxed |= line_err[i]; + end + end + end + + // Mux between incoming rdata and the muxed line data + assign output_data = |fill_data_rvd ? instr_rdata_i : line_data_muxed; + assign output_err = |fill_data_rvd ? instr_err_i : line_err_muxed; + + // Output data is valid (from any of the three possible sources). Note that fill_out_arb + // must be used here rather than fill_out_req because data can become valid out of order + // (e.g. cache hit data can become available ahead of an older outstanding miss). + assign data_valid = |fill_out_arb; + + // Skid buffer data + assign skid_data_d = output_data[31:16]; + + assign skid_en = data_valid & (ready_i | skid_ready); + + always_ff @(posedge clk_i) begin + if (skid_en) begin + skid_data_q <= skid_data_d; + skid_err_q <= output_err; + end + end + + // The data in the skid buffer is ready if it's a complete compressed instruction or if there's + // an error (no need to wait for the second half) + assign skid_complete_instr = skid_valid_q & ((skid_data_q[1:0] != 2'b11) | skid_err_q); + + // Data can be loaded into the skid buffer for an unaligned uncompressed instruction + assign skid_ready = output_addr_q[1] & ~skid_valid_q & (~output_compressed | output_err); + + assign output_ready = (ready_i | skid_ready) & ~skid_complete_instr; + + assign output_compressed = (rdata_o[1:0] != 2'b11); + + assign skid_valid_d = + // Branches invalidate the skid buffer + branch_or_mispredict ? 1'b0 : + // Once valid, the skid buffer stays valid until a compressed instruction realigns the stream + (skid_valid_q ? ~(ready_i & ((skid_data_q[1:0] != 2'b11) | skid_err_q)) : + // The skid buffer becomes valid when: + // - we branch to an unaligned uncompressed instruction + (((output_addr_q[1] & (~output_compressed | output_err)) | + // - a compressed instruction misaligns the stream + (~output_addr_q[1] & output_compressed & ~output_err & ready_i)) & data_valid)); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + skid_valid_q <= 1'b0; + end else begin + skid_valid_q <= skid_valid_d; + end + end + + // Signal that valid data is available to the IF stage + // Note that if the first half of an unaligned instruction reports an error, we do not need + // to wait for the second half (and for PMP errors we might not have fetched the second half) + // Compressed instruction completely satisfied by skid buffer + assign output_valid = skid_complete_instr | + // Output data available and, output stream aligned, or skid data available, + (data_valid & (~output_addr_q[1] | skid_valid_q | + // or this is an error or an unaligned compressed instruction + output_err | (output_data[17:16] != 2'b11))); + + // Update the address on branches and every time an instruction is driven + assign output_addr_en = branch_or_mispredict | (ready_i & valid_o); + + // Increment the address by two every time a compressed instruction is popped + assign addr_incr_two = output_compressed & ~err_o; + + // Next IF stage PC + assign output_addr_incr = (output_addr_q[31:1] + + // Increment address by 4 or 2 + {29'd0, ~addr_incr_two, addr_incr_two}); + + // Redirect the address on branches or mispredicts + assign output_addr_d = branch_i ? addr_i[31:1] : + branch_mispredict_i ? branch_mispredict_addr[31:1] : + output_addr_incr; + + always_ff @(posedge clk_i) begin + if (output_addr_en) begin + output_addr_q <= output_addr_d; + end + end + + // Mux the data from BusWidth to halfword + // This muxing realigns data when instruction words are split across BUS_W e.g. + // word 1 |----|*h1*| + // word 0 |*h0*|----| --> |*h1*|*h0*| + // 31 15 0 31 15 0 + always_comb begin + output_data_lo = '0; + for (int i = 0; i < OUTPUT_BEATS; i++) begin + if (output_addr_q[BUS_W-1:1] == i[BUS_W-2:0]) begin + output_data_lo |= output_data[i*16+:16]; + end + end + end + + always_comb begin + output_data_hi = '0; + for (int i = 0; i < OUTPUT_BEATS-1; i++) begin + if (output_addr_q[BUS_W-1:1] == i[BUS_W-2:0]) begin + output_data_hi |= output_data[(i+1)*16+:16]; + end + end + if (&output_addr_q[BUS_W-1:1]) begin + output_data_hi |= output_data[15:0]; + end + end + + assign valid_o = output_valid & ~branch_mispredict_i; + assign rdata_o = {output_data_hi, (skid_valid_q ? skid_data_q : output_data_lo)}; + assign addr_o = {output_addr_q, 1'b0}; + assign err_o = (skid_valid_q & skid_err_q) | (~skid_complete_instr & output_err); + // Error caused by the second half of a misaligned uncompressed instruction + // (only relevant when err_o is set) + assign err_plus2_o = skid_valid_q & ~skid_err_q; + + /////////////////// + // Invalidations // + /////////////////// + + // Invalidate on reset, or when instructed. If an invalidation request is received while a + // previous invalidation is ongoing, it does not need to be restarted. + assign start_inval = (~reset_inval_q | icache_inval_i) & ~inval_prog_q; + assign inval_prog_d = start_inval | (inval_prog_q & ~inval_done); + assign inval_done = &inval_index_q; + assign inval_index_d = start_inval ? '0 : + (inval_index_q + {{INDEX_W-1{1'b0}},1'b1}); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + inval_prog_q <= 1'b0; + reset_inval_q <= 1'b0; + end else begin + inval_prog_q <= inval_prog_d; + reset_inval_q <= 1'b1; + end + end + + always_ff @(posedge clk_i) begin + if (inval_prog_d) begin + inval_index_q <= inval_index_d; + end + end + + ///////////////// + // Busy status // + ///////////////// + + // Only busy (for WFI purposes) while an invalidation is in-progress, or external requests are + // outstanding. + assign busy_o = inval_prog_q | (|(fill_busy_q & ~fill_rvd_done)); + + + +endmodule
diff --git a/verilog/rtl/brq_ifu_prefetch_buffer.sv b/verilog/rtl/brq_ifu_prefetch_buffer.sv new file mode 100644 index 0000000..f366518 --- /dev/null +++ b/verilog/rtl/brq_ifu_prefetch_buffer.sv
@@ -0,0 +1,316 @@ + +/** + * Prefetcher Buffer for 32 bit memory interface + * + * Prefetch Buffer that caches instructions. This cuts overly long critical + * paths to the instruction cache. + */ +module brq_ifu_prefetch_buffer #( + parameter bit BranchPredictor = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + + input logic req_i, + + input logic branch_i, + input logic branch_spec_i, + input logic predicted_branch_i, + input logic branch_mispredict_i, + input logic [31:0] addr_i, + + + input logic ready_i, + output logic valid_o, + output logic [31:0] rdata_o, + output logic [31:0] addr_o, + output logic err_o, + output logic err_plus2_o, + + + // goes to instruction memory / instruction cache + output logic instr_req_o, + input logic instr_gnt_i, + output logic [31:0] instr_addr_o, + input logic [31:0] instr_rdata_i, + input logic instr_err_i, + input logic instr_pmp_err_i, + input logic instr_rvalid_i, + + // Prefetch Buffer Status + output logic busy_o +); + + localparam int unsigned NUM_REQS = 2; + + logic branch_suppress; + logic valid_new_req, valid_req; + logic valid_req_d, valid_req_q; + logic discard_req_d, discard_req_q; + logic gnt_or_pmp_err, rvalid_or_pmp_err; + logic [NUM_REQS-1:0] rdata_outstanding_n, rdata_outstanding_s, rdata_outstanding_q; + logic [NUM_REQS-1:0] branch_discard_n, branch_discard_s, branch_discard_q; + logic [NUM_REQS-1:0] rdata_pmp_err_n, rdata_pmp_err_s, rdata_pmp_err_q; + logic [NUM_REQS-1:0] rdata_outstanding_rev; + + logic [31:0] stored_addr_d, stored_addr_q; + logic stored_addr_en; + logic [31:0] fetch_addr_d, fetch_addr_q; + logic fetch_addr_en; + logic [31:0] branch_mispredict_addr; + logic [31:0] instr_addr, instr_addr_w_aligned; + logic instr_or_pmp_err; + + logic fifo_valid; + logic [31:0] fifo_addr; + logic fifo_ready; + logic fifo_clear; + logic [NUM_REQS-1:0] fifo_busy; + + logic valid_raw; + + logic [31:0] addr_next; + + logic branch_or_mispredict; + + //////////////////////////// + // Prefetch buffer status // + //////////////////////////// + + assign busy_o = (|rdata_outstanding_q) | instr_req_o; + + assign branch_or_mispredict = branch_i | branch_mispredict_i; + + ////////////////////////////////////////////// + // Fetch fifo - consumes addresses and data // + ////////////////////////////////////////////// + + // Instruction fetch errors are valid on the data phase of a request + // PMP errors are generated in the address phase, and registered into a fake data phase + assign instr_or_pmp_err = instr_err_i | rdata_pmp_err_q[0]; + + // A branch will invalidate any previously fetched instructions. + // Note that the FENCE.I instruction relies on this flushing behaviour on branch. If it is + // altered the FENCE.I implementation may require changes. + assign fifo_clear = branch_or_mispredict; + + // Reversed version of rdata_outstanding_q which can be overlaid with fifo fill state + for (genvar i = 0; i < NUM_REQS; i++) begin : gen_rd_rev + assign rdata_outstanding_rev[i] = rdata_outstanding_q[NUM_REQS-1-i]; + end + + // The fifo is ready to accept a new request if it is not full - including space reserved for + // requests already outstanding. + // Overlay the fifo fill state with the outstanding requests to see if there is space. + assign fifo_ready = ~&(fifo_busy | rdata_outstanding_rev); + + brq_ifu_fifo #( + .NUM_REQS (NUM_REQS) + ) fifo_i ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + + .clear_i ( fifo_clear ), + .busy_o ( fifo_busy ), + + .in_valid_i ( fifo_valid ), + .in_addr_i ( fifo_addr ), + .in_rdata_i ( instr_rdata_i ), + .in_err_i ( instr_or_pmp_err ), + + .out_valid_o ( valid_raw ), + .out_ready_i ( ready_i ), + .out_rdata_o ( rdata_o ), + .out_addr_o ( addr_o ), + .out_addr_next_o ( addr_next ), + .out_err_o ( err_o ), + .out_err_plus2_o ( err_plus2_o ) + ); + + ////////////// + // Requests // + ////////////// + + // Suppress a new request on a not-taken branch (as the external address will be incorrect) + assign branch_suppress = branch_spec_i & ~branch_i; + + // Make a new request any time there is space in the FIFO, and space in the request queue + assign valid_new_req = ~branch_suppress & req_i & (fifo_ready | branch_or_mispredict) & + ~rdata_outstanding_q[NUM_REQS-1]; + + assign valid_req = valid_req_q | valid_new_req; + + // If a request address triggers a PMP error, the external bus request is suppressed. We might + // therefore never receive a grant for such a request. The grant is faked in this case to make + // sure the request proceeds and the error is pushed to the FIFO. + assign gnt_or_pmp_err = instr_gnt_i | instr_pmp_err_i; + + // As with the grant, the rvalid must be faked for a PMP error, since the request was suppressed. + assign rvalid_or_pmp_err = rdata_outstanding_q[0] & (instr_rvalid_i | rdata_pmp_err_q[0]); + + // Hold the request stable for requests that didn't get granted + assign valid_req_d = valid_req & ~gnt_or_pmp_err; + + // Record whether an outstanding bus request is cancelled by a branch + assign discard_req_d = valid_req_q & (branch_or_mispredict | discard_req_q); + + //////////////// + // Fetch addr // + //////////////// + + // Two addresses are tracked in the prefetch buffer: + // 1. stored_addr_q - This is the address issued on the bus. It stays stable until + // the request is granted. + // 2. fetch_addr_q - This is our next address to fetch from. It is updated on branches to + // capture the new address, and then for each new request issued. + // A third address is tracked in the fetch FIFO itself: + // 3. instr_addr_q - This is the address at the head of the FIFO, efectively our oldest fetched + // address. This address is updated on branches, and does its own increment + // each time the FIFO is popped. + + // 1. stored_addr_q + + // Only update stored_addr_q for new ungranted requests + assign stored_addr_en = valid_new_req & ~valid_req_q & ~gnt_or_pmp_err; + + // Store whatever address was issued on the bus + assign stored_addr_d = instr_addr; + + // CPU resets with a branch, so no need to reset these addresses + always_ff @(posedge clk_i) begin + if (stored_addr_en) begin + stored_addr_q <= stored_addr_d; + end + end + + if (BranchPredictor) begin : g_branch_predictor + // Where the branch predictor is present record what address followed a predicted branch. If + // that branch is predicted taken but mispredicted (so not-taken) this is used to resume on + // the not-taken code path. + logic [31:0] branch_mispredict_addr_q; + logic branch_mispredict_addr_en; + + assign branch_mispredict_addr_en = branch_i & predicted_branch_i; + + always_ff @(posedge clk_i) begin + if (branch_mispredict_addr_en) begin + branch_mispredict_addr_q <= addr_next; + end + end + + assign branch_mispredict_addr = branch_mispredict_addr_q; + end else begin : g_no_branch_predictor + logic unused_predicted_branch; + logic [31:0] unused_addr_next; + + assign unused_predicted_branch = predicted_branch_i; + assign unused_addr_next = addr_next; + + assign branch_mispredict_addr = '0; + end + + // 2. fetch_addr_q + + // Update on a branch or as soon as a request is issued + assign fetch_addr_en = branch_or_mispredict | (valid_new_req & ~valid_req_q); + + assign fetch_addr_d = (branch_i ? addr_i : + branch_mispredict_i ? {branch_mispredict_addr[31:2], 2'b00} : + {fetch_addr_q[31:2], 2'b00}) + + // Current address + 4 + {{29{1'b0}},(valid_new_req & ~valid_req_q),2'b00}; + + always_ff @(posedge clk_i) begin + if (fetch_addr_en) begin + fetch_addr_q <= fetch_addr_d; + end + end + + // Address mux + assign instr_addr = valid_req_q ? stored_addr_q : + branch_spec_i ? addr_i : + branch_mispredict_i ? branch_mispredict_addr : + fetch_addr_q; + + assign instr_addr_w_aligned = {instr_addr[31:2], 2'b00}; + + /////////////////////////////// + // Request outstanding queue // + /////////////////////////////// + + for (genvar i = 0; i < NUM_REQS; i++) begin : g_outstanding_reqs + // Request 0 (always the oldest outstanding request) + if (i == 0) begin : g_req0 + // A request becomes outstanding once granted, and is cleared once the rvalid is received. + // Outstanding requests shift down the queue towards entry 0. + assign rdata_outstanding_n[i] = (valid_req & gnt_or_pmp_err) | + rdata_outstanding_q[i]; + // If a branch is received at any point while a request is outstanding, it must be tracked + // to ensure we discard the data once received + assign branch_discard_n[i] = (valid_req & gnt_or_pmp_err & discard_req_d) | + (branch_or_mispredict & rdata_outstanding_q[i]) | + branch_discard_q[i]; + // Record whether this request received a PMP error + assign rdata_pmp_err_n[i] = (valid_req & ~rdata_outstanding_q[i] & instr_pmp_err_i) | + rdata_pmp_err_q[i]; + + end else begin : g_reqtop + // Entries > 0 consider the FIFO fill state to calculate their next state (by checking + // whether the previous entry is valid) + + assign rdata_outstanding_n[i] = (valid_req & gnt_or_pmp_err & + rdata_outstanding_q[i-1]) | + rdata_outstanding_q[i]; + assign branch_discard_n[i] = (valid_req & gnt_or_pmp_err & discard_req_d & + rdata_outstanding_q[i-1]) | + (branch_or_mispredict & rdata_outstanding_q[i]) | + branch_discard_q[i]; + assign rdata_pmp_err_n[i] = (valid_req & ~rdata_outstanding_q[i] & instr_pmp_err_i & + rdata_outstanding_q[i-1]) | + rdata_pmp_err_q[i]; + end + end + + // Shift the entries down on each instr_rvalid_i + assign rdata_outstanding_s = rvalid_or_pmp_err ? {1'b0,rdata_outstanding_n[NUM_REQS-1:1]} : + rdata_outstanding_n; + assign branch_discard_s = rvalid_or_pmp_err ? {1'b0,branch_discard_n[NUM_REQS-1:1]} : + branch_discard_n; + assign rdata_pmp_err_s = rvalid_or_pmp_err ? {1'b0,rdata_pmp_err_n[NUM_REQS-1:1]} : + rdata_pmp_err_n; + + // Push a new entry to the FIFO once complete (and not cancelled by a branch) + assign fifo_valid = rvalid_or_pmp_err & ~branch_discard_q[0]; + + assign fifo_addr = branch_i ? addr_i : branch_mispredict_addr; + + /////////////// + // Registers // + /////////////// + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + valid_req_q <= 1'b0; + discard_req_q <= 1'b0; + rdata_outstanding_q <= 'b0; + branch_discard_q <= 'b0; + rdata_pmp_err_q <= 'b0; + end else begin + valid_req_q <= valid_req_d; + discard_req_q <= discard_req_d; + rdata_outstanding_q <= rdata_outstanding_s; + branch_discard_q <= branch_discard_s; + rdata_pmp_err_q <= rdata_pmp_err_s; + end + end + + ///////////// + // Outputs // + ///////////// + + assign instr_req_o = valid_req; + assign instr_addr_o = instr_addr_w_aligned; + + assign valid_o = valid_raw & ~branch_mispredict_i; + +endmodule
diff --git a/verilog/rtl/brq_lsu.sv b/verilog/rtl/brq_lsu.sv new file mode 100644 index 0000000..a87191a --- /dev/null +++ b/verilog/rtl/brq_lsu.sv
@@ -0,0 +1,489 @@ +/** + * Load Store Unit + * + * Load Store Unit, used to eliminate multiple access during processor stalls, + * and to align bytes and halfwords. + */ + +module brq_lsu +( + input logic clk_i, + input logic rst_ni, + + // data interface + output logic data_req_o, + input logic data_gnt_i, + input logic data_rvalid_i, + input logic data_err_i, + input logic data_pmp_err_i, + + output logic [31:0] data_addr_o, + output logic data_we_o, + output logic [3:0] data_be_o, + output logic [31:0] data_wdata_o, + input logic [31:0] data_rdata_i, + + // signals to/from ID/EX stage + input logic lsu_we_i, // write enable -> from ID/EX + input logic [1:0] lsu_type_i, // data type: word, half word, byte -> from ID/EX + input logic [31:0] lsu_wdata_i, // data to write to memory -> from ID/EX + input logic lsu_sign_ext_i, // sign extension -> from ID/EX + + output logic [31:0] lsu_rdata_o, // requested data -> to ID/EX + output logic lsu_rdata_valid_o, + input logic lsu_req_i, // data request -> from ID/EX + + input logic [31:0] adder_result_ex_i, // address computed in ALU -> from ID/EX + + output logic addr_incr_req_o, // request address increment for + // misaligned accesses -> to ID/EX + output logic [31:0] addr_last_o, // address of last transaction -> to controller + // -> mtval + // -> AGU for misaligned accesses + + output logic lsu_req_done_o, // Signals that data request is complete + // (only need to await final data + // response) -> to ID/EX + + output logic lsu_resp_valid_o, // LSU has response from transaction -> to ID/EX + + // exception signals + output logic load_err_o, + output logic store_err_o, + + output logic busy_o, + + output logic perf_load_o, + output logic perf_store_o +); + + logic [31:0] data_addr; + logic [31:0] data_addr_w_aligned; + logic [31:0] addr_last_q; + + logic addr_update; + logic ctrl_update; + logic rdata_update; + logic [31:8] rdata_q; + logic [1:0] rdata_offset_q; + logic [1:0] data_type_q; + logic data_sign_ext_q; + logic data_we_q; + + logic [1:0] data_offset; // mux control for data to be written to memory + + logic [3:0] data_be; + logic [31:0] data_wdata; + + logic [31:0] data_rdata_ext; + + logic [31:0] rdata_w_ext; // word realignment for misaligned loads + logic [31:0] rdata_h_ext; // sign extension for half words + logic [31:0] rdata_b_ext; // sign extension for bytes + + logic split_misaligned_access; + logic handle_misaligned_q, handle_misaligned_d; // high after receiving grant for first + // part of a misaligned access + logic pmp_err_q, pmp_err_d; + logic lsu_err_q, lsu_err_d; + logic data_or_pmp_err; + + typedef enum logic [2:0] { + IDLE, WAIT_GNT_MIS, WAIT_RVALID_MIS, WAIT_GNT, + WAIT_RVALID_MIS_GNTS_DONE + } ls_fsm_e; + + ls_fsm_e ls_fsm_cs, ls_fsm_ns; + + assign data_addr = adder_result_ex_i; + assign data_offset = data_addr[1:0]; + + /////////////////// + // BE generation // + /////////////////// + + always_comb begin + unique case (lsu_type_i) // Data type 00 Word, 01 Half word, 11,10 byte + 2'b00: begin // Writing a word + if (!handle_misaligned_q) begin // first part of potentially misaligned transaction + unique case (data_offset) + 2'b00: data_be = 4'b1111; + 2'b01: data_be = 4'b1110; + 2'b10: data_be = 4'b1100; + 2'b11: data_be = 4'b1000; + // default: data_be = 4'b1111; + endcase // case (data_offset) + end else begin // second part of misaligned transaction + unique case (data_offset) + 2'b00: data_be = 4'b0000; // this is not used, but included for completeness + 2'b01: data_be = 4'b0001; + 2'b10: data_be = 4'b0011; + 2'b11: data_be = 4'b0111; + // default: data_be = 4'b1111; + endcase // case (data_offset) + end + end + + 2'b01: begin // Writing a half word + if (!handle_misaligned_q) begin // first part of potentially misaligned transaction + unique case (data_offset) + 2'b00: data_be = 4'b0011; + 2'b01: data_be = 4'b0110; + 2'b10: data_be = 4'b1100; + 2'b11: data_be = 4'b1000; + // default: data_be = 4'b1111; + endcase // case (data_offset) + end else begin // second part of misaligned transaction + data_be = 4'b0001; + end + end + + 2'b10, + 2'b11: begin // Writing a byte + unique case (data_offset) + 2'b00: data_be = 4'b0001; + 2'b01: data_be = 4'b0010; + 2'b10: data_be = 4'b0100; + 2'b11: data_be = 4'b1000; + // default: data_be = 4'b1111; + endcase // case (data_offset) + end + + // default: data_be = 4'b1111; + endcase // case (lsu_type_i) + end + + ///////////////////// + // WData alignment // + ///////////////////// + + // prepare data to be written to the memory + // we handle misaligned accesses, half word and byte accesses here + always_comb begin + unique case (data_offset) + 2'b00: data_wdata = lsu_wdata_i[31:0]; + 2'b01: data_wdata = {lsu_wdata_i[23:0], lsu_wdata_i[31:24]}; + 2'b10: data_wdata = {lsu_wdata_i[15:0], lsu_wdata_i[31:16]}; + 2'b11: data_wdata = {lsu_wdata_i[ 7:0], lsu_wdata_i[31: 8]}; + // default: data_wdata = lsu_wdata_i[31:0]; + endcase // case (data_offset) + end + + ///////////////////// + // RData alignment // + ///////////////////// + + // register for unaligned rdata + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rdata_q <= '0; + end else if (rdata_update) begin + rdata_q <= data_rdata_i[31:8]; + end + end + + // registers for transaction control + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rdata_offset_q <= 2'h0; + data_type_q <= 2'h0; + data_sign_ext_q <= 1'b0; + data_we_q <= 1'b0; + end else if (ctrl_update) begin + rdata_offset_q <= data_offset; + data_type_q <= lsu_type_i; + data_sign_ext_q <= lsu_sign_ext_i; + data_we_q <= lsu_we_i; + end + end + + // Store last address for mtval + AGU for misaligned transactions. + // Do not update in case of errors, mtval needs the (first) failing address + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + addr_last_q <= '0; + end else if (addr_update) begin + addr_last_q <= data_addr; + end + end + + // take care of misaligned words + always_comb begin + unique case (rdata_offset_q) + 2'b00: rdata_w_ext = data_rdata_i[31:0]; + 2'b01: rdata_w_ext = {data_rdata_i[ 7:0], rdata_q[31:8]}; + 2'b10: rdata_w_ext = {data_rdata_i[15:0], rdata_q[31:16]}; + 2'b11: rdata_w_ext = {data_rdata_i[23:0], rdata_q[31:24]}; + // default: rdata_w_ext = data_rdata_i[31:0]; + endcase + end + + //////////////////// + // Sign extension // + //////////////////// + + // sign extension for half words + always_comb begin + unique case (rdata_offset_q) + 2'b00: begin + if (!data_sign_ext_q) begin + rdata_h_ext = {16'h0000, data_rdata_i[15:0]}; + end else begin + rdata_h_ext = {{16{data_rdata_i[15]}}, data_rdata_i[15:0]}; + end + end + + 2'b01: begin + if (!data_sign_ext_q) begin + rdata_h_ext = {16'h0000, data_rdata_i[23:8]}; + end else begin + rdata_h_ext = {{16{data_rdata_i[23]}}, data_rdata_i[23:8]}; + end + end + + 2'b10: begin + if (!data_sign_ext_q) begin + rdata_h_ext = {16'h0000, data_rdata_i[31:16]}; + end else begin + rdata_h_ext = {{16{data_rdata_i[31]}}, data_rdata_i[31:16]}; + end + end + + 2'b11: begin + if (!data_sign_ext_q) begin + rdata_h_ext = {16'h0000, data_rdata_i[7:0], rdata_q[31:24]}; + end else begin + rdata_h_ext = {{16{data_rdata_i[7]}}, data_rdata_i[7:0], rdata_q[31:24]}; + end + end + + // default: rdata_h_ext = {16'h0000, data_rdata_i[15:0]}; + endcase // case (rdata_offset_q) + end + + // sign extension for bytes + always_comb begin + unique case (rdata_offset_q) + 2'b00: begin + if (!data_sign_ext_q) begin + rdata_b_ext = {24'h00_0000, data_rdata_i[7:0]}; + end else begin + rdata_b_ext = {{24{data_rdata_i[7]}}, data_rdata_i[7:0]}; + end + end + + 2'b01: begin + if (!data_sign_ext_q) begin + rdata_b_ext = {24'h00_0000, data_rdata_i[15:8]}; + end else begin + rdata_b_ext = {{24{data_rdata_i[15]}}, data_rdata_i[15:8]}; + end + end + + 2'b10: begin + if (!data_sign_ext_q) begin + rdata_b_ext = {24'h00_0000, data_rdata_i[23:16]}; + end else begin + rdata_b_ext = {{24{data_rdata_i[23]}}, data_rdata_i[23:16]}; + end + end + + 2'b11: begin + if (!data_sign_ext_q) begin + rdata_b_ext = {24'h00_0000, data_rdata_i[31:24]}; + end else begin + rdata_b_ext = {{24{data_rdata_i[31]}}, data_rdata_i[31:24]}; + end + end + + // default: rdata_b_ext = {24'h00_0000, data_rdata_i[7:0]}; + endcase // case (rdata_offset_q) + end + + // select word, half word or byte sign extended version + always_comb begin + unique case (data_type_q) + 2'b00: data_rdata_ext = rdata_w_ext; + 2'b01: data_rdata_ext = rdata_h_ext; + 2'b10,2'b11: data_rdata_ext = rdata_b_ext; + // default: data_rdata_ext = rdata_w_ext; + endcase // case (data_type_q) + end + + ///////////// + // LSU FSM // + ///////////// + + // check for misaligned accesses that need to be split into two word-aligned accesses + assign split_misaligned_access = + ((lsu_type_i == 2'b00) && (data_offset != 2'b00)) || // misaligned word access + ((lsu_type_i == 2'b01) && (data_offset == 2'b11)); // misaligned half-word access + + // FSM + always_comb begin + ls_fsm_ns = ls_fsm_cs; + + data_req_o = 1'b0; + addr_incr_req_o = 1'b0; + handle_misaligned_d = handle_misaligned_q; + pmp_err_d = pmp_err_q; + lsu_err_d = lsu_err_q; + + addr_update = 1'b0; + ctrl_update = 1'b0; + rdata_update = 1'b0; + + perf_load_o = 1'b0; + perf_store_o = 1'b0; + + unique case (ls_fsm_cs) + + IDLE: begin + pmp_err_d = 1'b0; + if (lsu_req_i) begin + data_req_o = 1'b1; + pmp_err_d = data_pmp_err_i; + lsu_err_d = 1'b0; + perf_load_o = ~lsu_we_i; + perf_store_o = lsu_we_i; + + if (data_gnt_i) begin + ctrl_update = 1'b1; + addr_update = 1'b1; + handle_misaligned_d = split_misaligned_access; + ls_fsm_ns = split_misaligned_access ? WAIT_RVALID_MIS : IDLE; + end else begin + ls_fsm_ns = split_misaligned_access ? WAIT_GNT_MIS : WAIT_GNT; + end + end + end + + WAIT_GNT_MIS: begin + data_req_o = 1'b1; + // data_pmp_err_i is valid during the address phase of a request. An error will block the + // external request and so a data_gnt_i might never be signalled. The registered version + // pmp_err_q is only updated for new address phases and so can be used in WAIT_GNT* and + // WAIT_RVALID* states + if (data_gnt_i || pmp_err_q) begin + addr_update = 1'b1; + ctrl_update = 1'b1; + handle_misaligned_d = 1'b1; + ls_fsm_ns = WAIT_RVALID_MIS; + end + end + + WAIT_RVALID_MIS: begin + // push out second request + data_req_o = 1'b1; + // tell ID/EX stage to update the address + addr_incr_req_o = 1'b1; + + // first part rvalid is received, or gets a PMP error + if (data_rvalid_i || pmp_err_q) begin + // Update the PMP error for the second part + pmp_err_d = data_pmp_err_i; + // Record the error status of the first part + lsu_err_d = data_err_i | pmp_err_q; + // Capture the first rdata for loads + rdata_update = ~data_we_q; + // If already granted, wait for second rvalid + ls_fsm_ns = data_gnt_i ? IDLE : WAIT_GNT; + // Update the address for the second part, if no error + addr_update = data_gnt_i & ~(data_err_i | pmp_err_q); + // clear handle_misaligned if second request is granted + handle_misaligned_d = ~data_gnt_i; + end else begin + // first part rvalid is NOT received + if (data_gnt_i) begin + // second grant is received + ls_fsm_ns = WAIT_RVALID_MIS_GNTS_DONE; + handle_misaligned_d = 1'b0; + end + end + end + + WAIT_GNT: begin + // tell ID/EX stage to update the address + addr_incr_req_o = handle_misaligned_q; + data_req_o = 1'b1; + if (data_gnt_i || pmp_err_q) begin + ctrl_update = 1'b1; + // Update the address, unless there was an error + addr_update = ~lsu_err_q; + ls_fsm_ns = IDLE; + handle_misaligned_d = 1'b0; + end + end + + WAIT_RVALID_MIS_GNTS_DONE: begin + // tell ID/EX stage to update the address (to make sure the + // second address can be captured correctly for mtval and PMP checking) + addr_incr_req_o = 1'b1; + // Wait for the first rvalid, second request is already granted + if (data_rvalid_i) begin + // Update the pmp error for the second part + pmp_err_d = data_pmp_err_i; + // The first part cannot see a PMP error in this state + lsu_err_d = data_err_i; + // Now we can update the address for the second part if no error + addr_update = ~data_err_i; + // Capture the first rdata for loads + rdata_update = ~data_we_q; + // Wait for second rvalid + ls_fsm_ns = IDLE; + end + end + + default: begin + ls_fsm_ns = IDLE; + end + endcase + end + + assign lsu_req_done_o = (lsu_req_i | (ls_fsm_cs != IDLE)) & (ls_fsm_ns == IDLE); + + // registers for FSM + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ls_fsm_cs <= IDLE; + handle_misaligned_q <= '0; + pmp_err_q <= '0; + lsu_err_q <= '0; + end else begin + ls_fsm_cs <= ls_fsm_ns; + handle_misaligned_q <= handle_misaligned_d; + pmp_err_q <= pmp_err_d; + lsu_err_q <= lsu_err_d; + end + end + + ///////////// + // Outputs // + ///////////// + + assign data_or_pmp_err = lsu_err_q | data_err_i | pmp_err_q; + assign lsu_resp_valid_o = (data_rvalid_i | pmp_err_q) & (ls_fsm_cs == IDLE); + assign lsu_rdata_valid_o = (ls_fsm_cs == IDLE) & data_rvalid_i & ~data_or_pmp_err & ~data_we_q; + + // output to register file + assign lsu_rdata_o = data_rdata_ext; + + // output data address must be word aligned + assign data_addr_w_aligned = {data_addr[31:2], 2'b00}; + + // output to data interface + assign data_addr_o = data_addr_w_aligned; + assign data_wdata_o = data_wdata; + assign data_we_o = lsu_we_i; + assign data_be_o = data_be; + + // output to ID stage: mtval + AGU for misaligned transactions + assign addr_last_o = addr_last_q; + + // Signal a load or store error depending on the transaction type outstanding + assign load_err_o = data_or_pmp_err & ~data_we_q & lsu_resp_valid_o; + assign store_err_o = data_or_pmp_err & data_we_q & lsu_resp_valid_o; + + assign busy_o = (ls_fsm_cs != IDLE); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/brq_pkg.sv b/verilog/rtl/brq_pkg.sv new file mode 100644 index 0000000..fa44b1d --- /dev/null +++ b/verilog/rtl/brq_pkg.sv
@@ -0,0 +1,535 @@ +// Copyright lowRISC contributors. +// Copyright 2017 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 + +/** + * Package with constants used by Ibex + */ +package brq_pkg; + +///////////////////// +// Parameter Enums // +///////////////////// + +typedef enum integer { + RegFileFF = 0, + RegFileFPGA = 1, + RegFileLatch = 2 +} regfile_e; + +typedef enum integer { + RV32MNone = 0, + RV32MSlow = 1, + RV32MFast = 2, + RV32MSingleCycle = 3 +} rv32m_e; + +typedef enum integer { + RV32BNone = 0, + RV32BBalanced = 1, + RV32BFull = 2 +} rv32b_e; + +// floatig point +typedef enum integer { + RV32FNone = 0, + RV32FSingle = 1, + RV64FDouble = 2 + // RV32FQuad = 3 +} rvfloat_e; + +///////////// +// Opcodes // +///////////// + +typedef enum logic [6:0] { + OPCODE_LOAD = 7'h03, + OPCODE_MISC_MEM = 7'h0f, + OPCODE_OP_IMM = 7'h13, + OPCODE_AUIPC = 7'h17, + OPCODE_STORE = 7'h23, + OPCODE_OP = 7'h33, + OPCODE_LUI = 7'h37, + OPCODE_BRANCH = 7'h63, + OPCODE_JALR = 7'h67, + OPCODE_JAL = 7'h6f, + OPCODE_SYSTEM = 7'h73, + // Floating Point + OPCODE_LOAD_FP = 7'h07, + OPCODE_STORE_FP = 7'h27, + OPCODE_MADD_FP = 7'h43, + OPCODE_MSUB_FP = 7'h47, + OPCODE_NMSUB_FP = 7'h4b, + OPCODE_NMADD_FP = 7'h4f, + OPCODE_OP_FP = 7'h53 +} opcode_e; + + +//////////////////// +// ALU operations // +//////////////////// + +typedef enum logic [5:0] { + // Arithmetics + ALU_ADD, + ALU_SUB, + + // Logics + ALU_XOR, + ALU_OR, + ALU_AND, + // RV32B + ALU_XNOR, + ALU_ORN, + ALU_ANDN, + + // Shifts + ALU_SRA, + ALU_SRL, + ALU_SLL, + // RV32B + ALU_SRO, + ALU_SLO, + ALU_ROR, + ALU_ROL, + ALU_GREV, + ALU_GORC, + ALU_SHFL, + ALU_UNSHFL, + + // Comparisons + ALU_LT, + ALU_LTU, + ALU_GE, + ALU_GEU, + ALU_EQ, + ALU_NE, + // RV32B + ALU_MIN, + ALU_MINU, + ALU_MAX, + ALU_MAXU, + + // Pack + // RV32B + ALU_PACK, + ALU_PACKU, + ALU_PACKH, + + // Sign-Extend + // RV32B + ALU_SEXTB, + ALU_SEXTH, + + // Bitcounting + // RV32B + ALU_CLZ, + ALU_CTZ, + ALU_PCNT, + + // Set lower than + ALU_SLT, + ALU_SLTU, + + // Ternary Bitmanip Operations + // RV32B + ALU_CMOV, + ALU_CMIX, + ALU_FSL, + ALU_FSR, + + // Single-Bit Operations + // RV32B + ALU_SBSET, + ALU_SBCLR, + ALU_SBINV, + ALU_SBEXT, + + // Bit Extract / Deposit + // RV32B + ALU_BEXT, + ALU_BDEP, + + // Bit Field Place + // RV32B + ALU_BFP, + + // Carry-less Multiply + // RV32B + ALU_CLMUL, + ALU_CLMULR, + ALU_CLMULH, + + // Cyclic Redundancy Check + ALU_CRC32_B, + ALU_CRC32C_B, + ALU_CRC32_H, + ALU_CRC32C_H, + ALU_CRC32_W, + ALU_CRC32C_W +} alu_op_e; + +typedef enum logic [1:0] { + // Multiplier/divider + MD_OP_MULL, + MD_OP_MULH, + MD_OP_DIV, + MD_OP_REM +} md_op_e; + +// define which type instruction +// is catered +typedef enum logic { + SINGLE_FP, + DOUBLE_FP +} fp_type_e; + +////////////////////////////////// +// Control and status registers // +////////////////////////////////// + +// CSR operations +typedef enum logic [1:0] { + CSR_OP_READ, + CSR_OP_WRITE, + CSR_OP_SET, + CSR_OP_CLEAR +} csr_op_e; + +// Privileged mode +typedef enum logic[1:0] { + PRIV_LVL_M = 2'b11, + PRIV_LVL_H = 2'b10, + PRIV_LVL_S = 2'b01, + PRIV_LVL_U = 2'b00 +} priv_lvl_e; + +// Constants for the dcsr.xdebugver fields +typedef enum logic[3:0] { + XDEBUGVER_NO = 4'd0, // no external debug support + XDEBUGVER_STD = 4'd4, // external debug according to RISC-V debug spec + XDEBUGVER_NONSTD = 4'd15 // debug not conforming to RISC-V debug spec +} x_debug_ver_e; + +////////////// +// WB stage // +////////////// + +// Type of instruction present in writeback stage +typedef enum logic[1:0] { + WB_INSTR_LOAD, // Instruction is awaiting load data + WB_INSTR_STORE, // Instruction is awaiting store response + WB_INSTR_OTHER // Instruction doesn't fit into above categories +} wb_instr_type_e; + +////////////// +// ID stage // +////////////// + +// Operand a selection +typedef enum logic[1:0] { + OP_A_REG_A, + OP_A_FWD, + OP_A_CURRPC, + OP_A_IMM +} op_a_sel_e; + +// Immediate a selection +typedef enum logic { + IMM_A_Z, + IMM_A_ZERO +} imm_a_sel_e; + +// Operand b selection +typedef enum logic { + OP_B_REG_B, + OP_B_IMM +} op_b_sel_e; + +// Immediate b selection +typedef enum logic [2:0] { + IMM_B_I, + IMM_B_S, + IMM_B_B, + IMM_B_U, + IMM_B_J, + IMM_B_INCR_PC, + IMM_B_INCR_ADDR +} imm_b_sel_e; + +// Regfile write data selection +typedef enum logic { + RF_WD_EX, + RF_WD_CSR +} rf_wd_sel_e; + +////////////// +// IF stage // +////////////// + +// PC mux selection +typedef enum logic [2:0] { + PC_BOOT, + PC_JUMP, + PC_EXC, + PC_ERET, + PC_DRET, + PC_BP +} pc_sel_e; + +// Exception PC mux selection +typedef enum logic [1:0] { + EXC_PC_EXC, + EXC_PC_IRQ, + EXC_PC_DBD, + EXC_PC_DBG_EXC // Exception while in debug mode +} exc_pc_sel_e; + +// Interrupt requests +typedef struct packed { + logic irq_software; + logic irq_timer; + logic irq_external; + logic [14:0] irq_fast; // 15 fast interrupts, + // one interrupt is reserved for NMI (not visible through mip/mie) +} irqs_t; + +// Exception cause +typedef enum logic [5:0] { + EXC_CAUSE_IRQ_SOFTWARE_M = {1'b1, 5'd03}, + EXC_CAUSE_IRQ_TIMER_M = {1'b1, 5'd07}, + EXC_CAUSE_IRQ_EXTERNAL_M = {1'b1, 5'd11}, + // EXC_CAUSE_IRQ_FAST_0 = {1'b1, 5'd16}, + // EXC_CAUSE_IRQ_FAST_14 = {1'b1, 5'd30}, + EXC_CAUSE_IRQ_NM = {1'b1, 5'd31}, // == EXC_CAUSE_IRQ_FAST_15 + EXC_CAUSE_INSN_ADDR_MISA = {1'b0, 5'd00}, + EXC_CAUSE_INSTR_ACCESS_FAULT = {1'b0, 5'd01}, + EXC_CAUSE_ILLEGAL_INSN = {1'b0, 5'd02}, + EXC_CAUSE_BREAKPOINT = {1'b0, 5'd03}, + EXC_CAUSE_LOAD_ACCESS_FAULT = {1'b0, 5'd05}, + EXC_CAUSE_STORE_ACCESS_FAULT = {1'b0, 5'd07}, + EXC_CAUSE_ECALL_UMODE = {1'b0, 5'd08}, + EXC_CAUSE_ECALL_MMODE = {1'b0, 5'd11} +} exc_cause_e; + +// Debug cause +typedef enum logic [2:0] { + DBG_CAUSE_NONE = 3'h0, + DBG_CAUSE_EBREAK = 3'h1, + DBG_CAUSE_TRIGGER = 3'h2, + DBG_CAUSE_HALTREQ = 3'h3, + DBG_CAUSE_STEP = 3'h4 +} dbg_cause_e; + +// PMP constants +parameter int unsigned PMP_MAX_REGIONS = 16; +parameter int unsigned PMP_CFG_W = 8; + +// PMP acces type +parameter int unsigned PMP_I = 0; +parameter int unsigned PMP_D = 1; + +typedef enum logic [1:0] { + PMP_ACC_EXEC = 2'b00, + PMP_ACC_WRITE = 2'b01, + PMP_ACC_READ = 2'b10 +} pmp_req_e; + +// PMP cfg structures +typedef enum logic [1:0] { + PMP_MODE_OFF = 2'b00, + PMP_MODE_TOR = 2'b01, + PMP_MODE_NA4 = 2'b10, + PMP_MODE_NAPOT = 2'b11 +} pmp_cfg_mode_e; + +typedef struct packed { + logic lock; + pmp_cfg_mode_e mode; + logic exec; + logic write; + logic read; +} pmp_cfg_t; + +// CSRs +typedef enum logic[11:0] { + // Machine information + CSR_MHARTID = 12'hF14, + + // Machine trap setup + CSR_MSTATUS = 12'h300, + CSR_MISA = 12'h301, + CSR_MIE = 12'h304, + CSR_MTVEC = 12'h305, + + // Machine trap handling + CSR_MSCRATCH = 12'h340, + CSR_MEPC = 12'h341, + CSR_MCAUSE = 12'h342, + CSR_MTVAL = 12'h343, + CSR_MIP = 12'h344, + + // Physical memory protection + CSR_PMPCFG0 = 12'h3A0, + CSR_PMPCFG1 = 12'h3A1, + CSR_PMPCFG2 = 12'h3A2, + CSR_PMPCFG3 = 12'h3A3, + CSR_PMPADDR0 = 12'h3B0, + CSR_PMPADDR1 = 12'h3B1, + CSR_PMPADDR2 = 12'h3B2, + CSR_PMPADDR3 = 12'h3B3, + CSR_PMPADDR4 = 12'h3B4, + CSR_PMPADDR5 = 12'h3B5, + CSR_PMPADDR6 = 12'h3B6, + CSR_PMPADDR7 = 12'h3B7, + CSR_PMPADDR8 = 12'h3B8, + CSR_PMPADDR9 = 12'h3B9, + CSR_PMPADDR10 = 12'h3BA, + CSR_PMPADDR11 = 12'h3BB, + CSR_PMPADDR12 = 12'h3BC, + CSR_PMPADDR13 = 12'h3BD, + CSR_PMPADDR14 = 12'h3BE, + CSR_PMPADDR15 = 12'h3BF, + + // Debug trigger + CSR_TSELECT = 12'h7A0, + CSR_TDATA1 = 12'h7A1, + CSR_TDATA2 = 12'h7A2, + CSR_TDATA3 = 12'h7A3, + CSR_MCONTEXT = 12'h7A8, + CSR_SCONTEXT = 12'h7AA, + + // Debug/trace + CSR_DCSR = 12'h7b0, + CSR_DPC = 12'h7b1, + + // Debug + CSR_DSCRATCH0 = 12'h7b2, // optional + CSR_DSCRATCH1 = 12'h7b3, // optional + + // Machine Counter/Timers + CSR_MCOUNTINHIBIT = 12'h320, + CSR_MHPMEVENT3 = 12'h323, + CSR_MHPMEVENT4 = 12'h324, + CSR_MHPMEVENT5 = 12'h325, + CSR_MHPMEVENT6 = 12'h326, + CSR_MHPMEVENT7 = 12'h327, + CSR_MHPMEVENT8 = 12'h328, + CSR_MHPMEVENT9 = 12'h329, + CSR_MHPMEVENT10 = 12'h32A, + CSR_MHPMEVENT11 = 12'h32B, + CSR_MHPMEVENT12 = 12'h32C, + CSR_MHPMEVENT13 = 12'h32D, + CSR_MHPMEVENT14 = 12'h32E, + CSR_MHPMEVENT15 = 12'h32F, + CSR_MHPMEVENT16 = 12'h330, + CSR_MHPMEVENT17 = 12'h331, + CSR_MHPMEVENT18 = 12'h332, + CSR_MHPMEVENT19 = 12'h333, + CSR_MHPMEVENT20 = 12'h334, + CSR_MHPMEVENT21 = 12'h335, + CSR_MHPMEVENT22 = 12'h336, + CSR_MHPMEVENT23 = 12'h337, + CSR_MHPMEVENT24 = 12'h338, + CSR_MHPMEVENT25 = 12'h339, + CSR_MHPMEVENT26 = 12'h33A, + CSR_MHPMEVENT27 = 12'h33B, + CSR_MHPMEVENT28 = 12'h33C, + CSR_MHPMEVENT29 = 12'h33D, + CSR_MHPMEVENT30 = 12'h33E, + CSR_MHPMEVENT31 = 12'h33F, + CSR_MCYCLE = 12'hB00, + CSR_MINSTRET = 12'hB02, + CSR_MHPMCOUNTER3 = 12'hB03, + CSR_MHPMCOUNTER4 = 12'hB04, + CSR_MHPMCOUNTER5 = 12'hB05, + CSR_MHPMCOUNTER6 = 12'hB06, + CSR_MHPMCOUNTER7 = 12'hB07, + CSR_MHPMCOUNTER8 = 12'hB08, + CSR_MHPMCOUNTER9 = 12'hB09, + CSR_MHPMCOUNTER10 = 12'hB0A, + CSR_MHPMCOUNTER11 = 12'hB0B, + CSR_MHPMCOUNTER12 = 12'hB0C, + CSR_MHPMCOUNTER13 = 12'hB0D, + CSR_MHPMCOUNTER14 = 12'hB0E, + CSR_MHPMCOUNTER15 = 12'hB0F, + CSR_MHPMCOUNTER16 = 12'hB10, + CSR_MHPMCOUNTER17 = 12'hB11, + CSR_MHPMCOUNTER18 = 12'hB12, + CSR_MHPMCOUNTER19 = 12'hB13, + CSR_MHPMCOUNTER20 = 12'hB14, + CSR_MHPMCOUNTER21 = 12'hB15, + CSR_MHPMCOUNTER22 = 12'hB16, + CSR_MHPMCOUNTER23 = 12'hB17, + CSR_MHPMCOUNTER24 = 12'hB18, + CSR_MHPMCOUNTER25 = 12'hB19, + CSR_MHPMCOUNTER26 = 12'hB1A, + CSR_MHPMCOUNTER27 = 12'hB1B, + CSR_MHPMCOUNTER28 = 12'hB1C, + CSR_MHPMCOUNTER29 = 12'hB1D, + CSR_MHPMCOUNTER30 = 12'hB1E, + CSR_MHPMCOUNTER31 = 12'hB1F, + CSR_MCYCLEH = 12'hB80, + CSR_MINSTRETH = 12'hB82, + CSR_MHPMCOUNTER3H = 12'hB83, + CSR_MHPMCOUNTER4H = 12'hB84, + CSR_MHPMCOUNTER5H = 12'hB85, + CSR_MHPMCOUNTER6H = 12'hB86, + CSR_MHPMCOUNTER7H = 12'hB87, + CSR_MHPMCOUNTER8H = 12'hB88, + CSR_MHPMCOUNTER9H = 12'hB89, + CSR_MHPMCOUNTER10H = 12'hB8A, + CSR_MHPMCOUNTER11H = 12'hB8B, + CSR_MHPMCOUNTER12H = 12'hB8C, + CSR_MHPMCOUNTER13H = 12'hB8D, + CSR_MHPMCOUNTER14H = 12'hB8E, + CSR_MHPMCOUNTER15H = 12'hB8F, + CSR_MHPMCOUNTER16H = 12'hB90, + CSR_MHPMCOUNTER17H = 12'hB91, + CSR_MHPMCOUNTER18H = 12'hB92, + CSR_MHPMCOUNTER19H = 12'hB93, + CSR_MHPMCOUNTER20H = 12'hB94, + CSR_MHPMCOUNTER21H = 12'hB95, + CSR_MHPMCOUNTER22H = 12'hB96, + CSR_MHPMCOUNTER23H = 12'hB97, + CSR_MHPMCOUNTER24H = 12'hB98, + CSR_MHPMCOUNTER25H = 12'hB99, + CSR_MHPMCOUNTER26H = 12'hB9A, + CSR_MHPMCOUNTER27H = 12'hB9B, + CSR_MHPMCOUNTER28H = 12'hB9C, + CSR_MHPMCOUNTER29H = 12'hB9D, + CSR_MHPMCOUNTER30H = 12'hB9E, + CSR_MHPMCOUNTER31H = 12'hB9F, + CSR_CPUCTRL = 12'h7C0, + CSR_SECURESEED = 12'h7C1, + + // Floating point fcsr + CSR_FCSR = 12'h003, + CSR_FRM = 12'h002, + CSR_FFLAG = 12'h001 +} csr_num_e; + +// CSR pmp-related offsets +parameter logic [11:0] CSR_OFF_PMP_CFG = 12'h3A0; // pmp_cfg @ 12'h3a0 - 12'h3a3 +parameter logic [11:0] CSR_OFF_PMP_ADDR = 12'h3B0; // pmp_addr @ 12'h3b0 - 12'h3bf + +// CSR status bits +parameter int unsigned CSR_MSTATUS_MIE_BIT = 3; +parameter int unsigned CSR_MSTATUS_MPIE_BIT = 7; +parameter int unsigned CSR_MSTATUS_MPP_BIT_LOW = 11; +parameter int unsigned CSR_MSTATUS_MPP_BIT_HIGH = 12; +parameter int unsigned CSR_MSTATUS_MPRV_BIT = 17; +parameter int unsigned CSR_MSTATUS_TW_BIT = 21; + +// CSR machine ISA +parameter logic [1:0] CSR_MISA_MXL = 2'd1; // M-XLEN: XLEN in M-Mode for RV32 + +// CSR interrupt pending/enable bits +parameter int unsigned CSR_MSIX_BIT = 3; +parameter int unsigned CSR_MTIX_BIT = 7; +parameter int unsigned CSR_MEIX_BIT = 11; +parameter int unsigned CSR_MFIX_BIT_LOW = 16; +parameter int unsigned CSR_MFIX_BIT_HIGH = 30; + +endpackage
diff --git a/verilog/rtl/brq_pmp.sv b/verilog/rtl/brq_pmp.sv new file mode 100644 index 0000000..0a5e800 --- /dev/null +++ b/verilog/rtl/brq_pmp.sv
@@ -0,0 +1,125 @@ + +module brq_pmp #( + // Granularity of NAPOT access, + // 0 = No restriction, 1 = 8 byte, 2 = 16 byte, 3 = 32 byte, etc. + parameter int unsigned PMPGranularity = 0, + // Number of access channels (e.g. i-side + d-side) + parameter int unsigned PMPNumChan = 2, + // Number of implemented regions + parameter int unsigned PMPNumRegions = 4 +) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + // Interface to CSRs + input brq_pkg::pmp_cfg_t csr_pmp_cfg_i [PMPNumRegions], + input logic [33:0] csr_pmp_addr_i [PMPNumRegions], + + input brq_pkg::priv_lvl_e priv_mode_i [PMPNumChan], + // Access checking channels + input logic [33:0] pmp_req_addr_i [PMPNumChan], + input brq_pkg::pmp_req_e pmp_req_type_i [PMPNumChan], + output logic pmp_req_err_o [PMPNumChan] + +); + + import brq_pkg::*; + + // Access Checking Signals + logic [33:0] region_start_addr [PMPNumRegions]; + logic [33:PMPGranularity+2] region_addr_mask [PMPNumRegions]; + logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_gt; + logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_lt; + logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_eq; + logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_match_all; + logic [PMPNumChan-1:0][PMPNumRegions-1:0] region_perm_check; + logic [PMPNumChan-1:0] access_fault; + + + // --------------- + // Access checking + // --------------- + + for (genvar r = 0; r < PMPNumRegions; r++) begin : g_addr_exp + // Start address for TOR matching + if (r == 0) begin : g_entry0 + assign region_start_addr[r] = (csr_pmp_cfg_i[r].mode == PMP_MODE_TOR) ? 34'h000000000 : + csr_pmp_addr_i[r]; + end else begin : g_oth + assign region_start_addr[r] = (csr_pmp_cfg_i[r].mode == PMP_MODE_TOR) ? csr_pmp_addr_i[r-1] : + csr_pmp_addr_i[r]; + end + // Address mask for NA matching + for (genvar b = PMPGranularity+2; b < 34; b++) begin : g_bitmask + if (b == 2) begin : g_bit0 + // Always mask bit 2 for NAPOT + assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT); + end else begin : g_others + // We will mask this bit if it is within the programmed granule + // i.e. addr = yyyy 0111 + // ^ + // | This bit pos is the top of the mask, all lower bits set + // thus mask = 1111 0000 + assign region_addr_mask[r][b] = (csr_pmp_cfg_i[r].mode != PMP_MODE_NAPOT) | + ~&csr_pmp_addr_i[r][b-1:PMPGranularity+1]; + end + end + end + + for (genvar c = 0; c < PMPNumChan; c++) begin : g_access_check + for (genvar r = 0; r < PMPNumRegions; r++) begin : g_regions + // Comparators are sized according to granularity + assign region_match_eq[c][r] = (pmp_req_addr_i[c][33:PMPGranularity+2] & + region_addr_mask[r]) == + (region_start_addr[r][33:PMPGranularity+2] & + region_addr_mask[r]); + assign region_match_gt[c][r] = pmp_req_addr_i[c][33:PMPGranularity+2] > + region_start_addr[r][33:PMPGranularity+2]; + assign region_match_lt[c][r] = pmp_req_addr_i[c][33:PMPGranularity+2] < + csr_pmp_addr_i[r][33:PMPGranularity+2]; + + always_comb begin + region_match_all[c][r] = 1'b0; + unique case (csr_pmp_cfg_i[r].mode) + PMP_MODE_OFF : region_match_all[c][r] = 1'b0; + PMP_MODE_NA4 : region_match_all[c][r] = region_match_eq[c][r]; + PMP_MODE_NAPOT : region_match_all[c][r] = region_match_eq[c][r]; + PMP_MODE_TOR : begin + region_match_all[c][r] = (region_match_eq[c][r] | region_match_gt[c][r]) & + region_match_lt[c][r]; + end + default : region_match_all[c][r] = 1'b0; + endcase + end + + // Check specific required permissions + assign region_perm_check[c][r] = + ((pmp_req_type_i[c] == PMP_ACC_EXEC) & csr_pmp_cfg_i[r].exec) | + ((pmp_req_type_i[c] == PMP_ACC_WRITE) & csr_pmp_cfg_i[r].write) | + ((pmp_req_type_i[c] == PMP_ACC_READ) & csr_pmp_cfg_i[r].read); + end + + // Access fault determination / prioritization + always_comb begin + // Default is allow for M-mode, deny for other modes + access_fault[c] = (priv_mode_i[c] != PRIV_LVL_M); + + // PMP entries are statically prioritized, from 0 to N-1 + // The lowest-numbered PMP entry which matches an address determines accessability + for (int r = PMPNumRegions-1; r >= 0; r--) begin + if (region_match_all[c][r]) begin + access_fault[c] = (priv_mode_i[c] == PRIV_LVL_M) ? + // For M-mode, any region which matches with the L-bit clear, or with sufficient + // access permissions will be allowed + (csr_pmp_cfg_i[r].lock & ~region_perm_check[c][r]) : + // For other modes, the lock bit doesn't matter + ~region_perm_check[c][r]; + end + end + end + + assign pmp_req_err_o[c] = access_fault[c]; + end + +endmodule
diff --git a/verilog/rtl/brq_register_file_ff.sv b/verilog/rtl/brq_register_file_ff.sv new file mode 100644 index 0000000..9aa643e --- /dev/null +++ b/verilog/rtl/brq_register_file_ff.sv
@@ -0,0 +1,98 @@ + +/** + * RISC-V register file + * + * Register file with 31 or 15x 32 bit wide registers. Register 0 is fixed to 0. + * This register file is based on flip flops. Use this register file when + * targeting FPGA synthesis or Verilator simulation. + */ +module brq_register_file_ff #( + parameter bit RV32E = 0, + parameter int unsigned DataWidth = 32, + parameter bit DummyInstructions = 0 +) ( + // Clock and Reset + input logic clk_i, + input logic rst_ni, + + input logic test_en_i, + input logic dummy_instr_id_i, + + //Read port R1 + input logic [4:0] raddr_a_i, + output logic [DataWidth-1:0] rdata_a_o, + + //Read port R2 + input logic [4:0] raddr_b_i, + output logic [DataWidth-1:0] rdata_b_o, + + + // Write port W1 + input logic [4:0] waddr_a_i, + input logic [DataWidth-1:0] wdata_a_i, + input logic we_a_i + +); + + localparam int unsigned ADDR_WIDTH = RV32E ? 4 : 5; + localparam int unsigned NUM_WORDS = 2**ADDR_WIDTH; + + logic [NUM_WORDS-1:0][DataWidth-1:0] rf_reg; + logic [NUM_WORDS-1:1][DataWidth-1:0] rf_reg_q; + logic [NUM_WORDS-1:1] we_a_dec; + + always_comb begin : we_a_decoder + for (int unsigned i = 1; i < NUM_WORDS; i++) begin + we_a_dec[i] = (waddr_a_i == 5'(i)) ? we_a_i : 1'b0; + end + end + + // No flops for R0 as it's hard-wired to 0 + for (genvar i = 1; i < NUM_WORDS; i++) begin : g_rf_flops + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rf_reg_q[i] <= '0; + end else if(we_a_dec[i]) begin + rf_reg_q[i] <= wdata_a_i; + end + end + end + + // With dummy instructions enabled, R0 behaves as a real register but will always return 0 for + // real instructions. + if (DummyInstructions) begin : g_dummy_r0 + logic we_r0_dummy; + logic [DataWidth-1:0] rf_r0_q; + + // Write enable for dummy R0 register (waddr_a_i will always be 0 for dummy instructions) + assign we_r0_dummy = we_a_i & dummy_instr_id_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rf_r0_q <= '0; + end else if (we_r0_dummy) begin + rf_r0_q <= wdata_a_i; + end + end + + // Output the dummy data for dummy instructions, otherwise R0 reads as zero + assign rf_reg[0] = dummy_instr_id_i ? rf_r0_q : '0; + + end else begin : g_normal_r0 + logic unused_dummy_instr_id; + assign unused_dummy_instr_id = dummy_instr_id_i; + + // R0 is nil + assign rf_reg[0] = '0; + end + + assign rf_reg[NUM_WORDS-1:1] = rf_reg_q[NUM_WORDS-1:1]; + + assign rdata_a_o = rf_reg[raddr_a_i]; + assign rdata_b_o = rf_reg[raddr_b_i]; + + // Signal not used in FF register file + logic unused_test_en; + assign unused_test_en = test_en_i; + +endmodule
diff --git a/verilog/rtl/brq_wbu.sv b/verilog/rtl/brq_wbu.sv new file mode 100644 index 0000000..95e801c --- /dev/null +++ b/verilog/rtl/brq_wbu.sv
@@ -0,0 +1,219 @@ + +/** + * Writeback Stage + * + * Writeback is an optional third pipeline stage. It writes data back to the register file that was + * produced in the ID/EX stage or awaits a response to a load/store (LSU writes direct to register + * file for load data). If the writeback stage is not present (WritebackStage == 0) this acts as + * a simple passthrough to write data direct to the register file. + */ + + +module brq_wbu #( + parameter bit WritebackStage = 1'b0 +) ( + input logic clk_i, + input logic rst_ni, + + input logic en_wb_i, + input brq_pkg::wb_instr_type_e instr_type_wb_i, + input logic [31:0] pc_id_i, + input logic instr_is_compressed_id_i, + input logic instr_perf_count_id_i, + + output logic ready_wb_o, + output logic rf_write_wb_o, + output logic outstanding_load_wb_o, + output logic outstanding_store_wb_o, + output logic [31:0] pc_wb_o, + output logic perf_instr_ret_wb_o, + output logic perf_instr_ret_compressed_wb_o, + + input logic [4:0] rf_waddr_id_i, + input logic [31:0] rf_wdata_id_i, + input logic rf_we_id_i, + + input logic [31:0] rf_wdata_lsu_i, + input logic rf_we_lsu_i, + + output logic [31:0] rf_wdata_fwd_wb_o, + + output logic [4:0] rf_waddr_wb_o, + output logic [31:0] rf_wdata_wb_o, + output logic rf_we_wb_o, + + input logic lsu_resp_valid_i, + input logic lsu_resp_err_i, + + output logic instr_done_wb_o, + + // floating point + output logic fp_rf_write_wb_o, + output logic fp_rf_wen_wb_o, + output logic [4:0] fp_rf_waddr_wb_o, + input logic [4:0] fp_rf_waddr_id_i, + input logic fp_rf_wen_id_i, + output logic [31:0] fp_rf_wdata_wb_o, + output logic fp_load_i +); + + import brq_pkg::*; + + // 0 == RF write from ID + // 1 == RF write from LSU + logic [31:0] rf_wdata_wb_mux[2]; + logic [1:0] rf_wdata_wb_mux_we; + + logic [31:0] fp_rf_wdata_wb_mux[2]; + logic [1:0] fp_rf_wdata_wb_mux_we; + + if(WritebackStage) begin : g_writeback_stage + logic [31:0] rf_wdata_wb_q; + logic rf_we_wb_q; + logic [4:0] rf_waddr_wb_q; + + logic wb_done; + + logic wb_valid_q; + logic [31:0] wb_pc_q; + logic wb_compressed_q; + logic wb_count_q; + wb_instr_type_e wb_instr_type_q; + + logic wb_valid_d; + + // floating point + //logic [31:0] fp_rf_wdata_wb_q; + logic fp_rf_we_wb_q; + //logic [4:0] fp_rf_waddr_wb_q; + logic fp_load_q; + + // Stage becomes valid if an instruction enters for ID/EX and valid is cleared when instruction + // is done + assign wb_valid_d = (en_wb_i & ready_wb_o) | (wb_valid_q & ~wb_done); + + // Writeback for non load/store instructions always completes in a cycle (so instantly done) + // Writeback for load/store must wait for response to be received by the LSU + // Signal only relevant if wb_valid_q set + assign wb_done = (wb_instr_type_q == WB_INSTR_OTHER) | lsu_resp_valid_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + wb_valid_q <= 1'b0; + end else begin + wb_valid_q <= wb_valid_d; + end + end + + always_ff @(posedge clk_i) begin + if(en_wb_i) begin + rf_we_wb_q <= rf_we_id_i; + rf_waddr_wb_q <= rf_waddr_id_i; + rf_wdata_wb_q <= rf_wdata_id_i; + wb_instr_type_q <= instr_type_wb_i; + wb_pc_q <= pc_id_i; + wb_compressed_q <= instr_is_compressed_id_i; + wb_count_q <= instr_perf_count_id_i; + + // added for floating point registers for wb stage + fp_rf_we_wb_q <= fp_rf_wen_id_i; + // fp_rf_waddr_wb_q <= rf_waddr_id_i; + //fp_rf_wdata_wb_q <= rf_wdata_id_i; + fp_load_q <= fp_load_i; + end + end + + assign rf_waddr_wb_o = rf_waddr_wb_q; + assign rf_wdata_wb_mux[0] = rf_wdata_wb_q; + assign rf_wdata_wb_mux_we[0] = rf_we_wb_q & wb_valid_q; + + assign fp_rf_waddr_wb_o = rf_waddr_wb_q; // no seperate datapath for rd address + assign fp_rf_wdata_wb_mux[0] = rf_wdata_wb_q; // no seperate datapath for data bus + assign fp_rf_wdata_wb_mux_we[0] = fp_rf_we_wb_q & wb_valid_q; + + assign ready_wb_o = ~wb_valid_q | wb_done; + + // Instruction in writeback will be writing to register file if either rf_we is set or writeback + // is awaiting load data. This is used for determining RF read hazards in ID/EX + assign rf_write_wb_o = wb_valid_q & (rf_we_wb_q | (wb_instr_type_q == WB_INSTR_LOAD)); + assign fp_rf_write_wb_o = wb_valid_q & (fp_rf_we_wb_q | (wb_instr_type_q == WB_INSTR_LOAD)); + + assign outstanding_load_wb_o = wb_valid_q & (wb_instr_type_q == WB_INSTR_LOAD); + assign outstanding_store_wb_o = wb_valid_q & (wb_instr_type_q == WB_INSTR_STORE); + + assign pc_wb_o = wb_pc_q; + + assign instr_done_wb_o = wb_valid_q & wb_done; + + // Increment instruction retire counters for valid instructions which are not lsu errors + assign perf_instr_ret_wb_o = instr_done_wb_o & wb_count_q & + ~(lsu_resp_valid_i & lsu_resp_err_i); + assign perf_instr_ret_compressed_wb_o = perf_instr_ret_wb_o & wb_compressed_q; + + // Forward data that will be written to the RF back to ID to resolve data hazards. The flopped + // rf_wdata_wb_q is used rather than rf_wdata_wb_o as the latter includes read data from memory + // that returns too late to be used on the forwarding path. + assign rf_wdata_fwd_wb_o = rf_wdata_wb_q; + + assign rf_wdata_wb_mux[1] = rf_wdata_lsu_i; + assign rf_wdata_wb_mux_we[1] = rf_we_lsu_i & ~fp_load_q; + + assign fp_rf_wdata_wb_mux[1] = rf_wdata_lsu_i; + assign fp_rf_wdata_wb_mux_we[1] = rf_we_lsu_i & fp_load_q; + end else begin : g_bypass_wb + // without writeback stage just pass through register write signals + assign rf_waddr_wb_o = rf_waddr_id_i; + assign rf_wdata_wb_mux[0] = rf_wdata_id_i; + assign rf_wdata_wb_mux_we[0] = rf_we_id_i; + + // for floating point unit + assign fp_rf_waddr_wb_o = rf_waddr_id_i; // no seperate datapath for rd address + assign fp_rf_wdata_wb_mux[0] = rf_wdata_id_i; // no seperate datapath for data bus + assign fp_rf_wdata_wb_mux_we[0] = fp_rf_wen_id_i; + + // Increment instruction retire counters for valid instructions which are not lsu errors + assign perf_instr_ret_wb_o = instr_perf_count_id_i & en_wb_i & + ~(lsu_resp_valid_i & lsu_resp_err_i); + assign perf_instr_ret_compressed_wb_o = perf_instr_ret_wb_o & instr_is_compressed_id_i; + + // ready needs to be constant 1 without writeback stage (otherwise ID/EX stage will stall) + assign ready_wb_o = 1'b1; + + // Unused Writeback stage only IO & wiring + // Assign inputs and internal wiring to unused signals to satisfy lint checks + // Tie-off outputs to constant values + logic unused_clk; + logic unused_rst; + wb_instr_type_e unused_instr_type_wb; + logic [31:0] unused_pc_id; + + assign unused_clk = clk_i; + assign unused_rst = rst_ni; + assign unused_instr_type_wb = instr_type_wb_i; + assign unused_pc_id = pc_id_i; + + assign outstanding_load_wb_o = 1'b0; + assign outstanding_store_wb_o = 1'b0; + assign pc_wb_o = '0; + assign rf_write_wb_o = 1'b0; + assign rf_wdata_fwd_wb_o = 32'b0; + assign instr_done_wb_o = 1'b0; + + assign rf_wdata_wb_mux[1] = rf_wdata_lsu_i; + assign rf_wdata_wb_mux_we[1] = rf_we_lsu_i & ~fp_load_i; + + assign fp_rf_wdata_wb_mux[1] = rf_wdata_lsu_i; + assign fp_rf_wdata_wb_mux_we[1] = rf_we_lsu_i & fp_load_i; + end + + // RF write data can come from ID results (all RF writes that aren't because of loads will come + // from here) or the LSU (RF writes for load data) + assign rf_wdata_wb_o = (rf_wdata_wb_mux_we[0]) ? rf_wdata_wb_mux[0] : + rf_wdata_wb_mux[1]; + assign rf_we_wb_o = |rf_wdata_wb_mux_we; + + assign fp_rf_wdata_wb_o = fp_rf_wdata_wb_mux_we[0] ? fp_rf_wdata_wb_mux[0] : + fp_rf_wdata_wb_mux[1]; + assign fp_rf_wen_wb_o = |fp_rf_wdata_wb_mux_we; + +endmodule
diff --git a/verilog/rtl/cf_math_pkg.sv b/verilog/rtl/cf_math_pkg.sv new file mode 100644 index 0000000..9f35a44 --- /dev/null +++ b/verilog/rtl/cf_math_pkg.sv
@@ -0,0 +1,61 @@ +// Copyright 2016 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +/// cf_math_pkg: Constant Function Implementations of Mathematical Functions for HDL Elaboration +/// +/// This package contains a collection of mathematical functions that are commonly used when defining +/// the value of constants in HDL code. These functions are implemented as Verilog constants +/// functions. Introduced in Verilog 2001 (IEEE Std 1364-2001), a constant function (§ 10.3.5) is a +/// function whose value can be evaluated at compile time or during elaboration. A constant function +/// must be called with arguments that are constants. +package cf_math_pkg; + + /// Ceiled Division of Two Natural Numbers + /// + /// Returns the quotient of two natural numbers, rounded towards plus infinity. + function automatic integer ceil_div (input longint dividend, input longint divisor); + automatic longint remainder; + + // pragma translate_off + `ifndef VERILATOR + if (dividend < 0) begin + $fatal(1, "Dividend %0d is not a natural number!", dividend); + end + + if (divisor < 0) begin + $fatal(1, "Divisor %0d is not a natural number!", divisor); + end + + if (divisor == 0) begin + $fatal(1, "Division by zero!"); + end + `endif + // pragma translate_on + + remainder = dividend; + for (ceil_div = 0; remainder > 0; ceil_div++) begin + remainder = remainder - divisor; + end + endfunction + + /// Index width required to be able to represent up to `num_idx` indices as a binary + /// encoded signal. + /// Ensures that the minimum width if an index signal is `1`, regardless of parametrization. + /// + /// Sample usage in type definition: + /// As parameter: + /// `parameter type idx_t = logic[cf_math_pkg::idx_width(NumIdx)-1:0]` + /// As typedef: + /// `typedef logic [cf_math_pkg::idx_width(NumIdx)-1:0] idx_t` + function automatic integer unsigned idx_width (input integer unsigned num_idx); + return (num_idx > 32'd1) ? unsigned'($clog2(num_idx)) : 32'd1; + endfunction + +endpackage
diff --git a/verilog/rtl/control_mvp.sv b/verilog/rtl/control_mvp.sv new file mode 100644 index 0000000..4c5364d --- /dev/null +++ b/verilog/rtl/control_mvp.sv
@@ -0,0 +1,3413 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// Company: IIS @ ETHZ - Federal Institute of Technology // +// // +// Engineers: Lei Li lile@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 04/03/2018 // +// Design Name: FPU // +// Module Name: control_mvp.sv // +// Project Name: Private FPU // +// Language: SystemVerilog // +// // +// Description: the control logic of div and sqrt // +// // +// Revision Date: 12/04/2018 // +// Lei Li // +// To address some requirements by Stefan and add low power // +// control for special cases // +// Revision Date: 13/04/2018 // +// Lei Li // +// To fix some bug found in Control FSM // +// when Iteration_unit_num_S = 2'b10 // +// // +// // +// // +//////////////////////////////////////////////////////////////////////////////// + +import defs_div_sqrt_mvp::*; + +module control_mvp + + (//Input + input logic Clk_CI, + input logic Rst_RBI, + input logic Div_start_SI , + input logic Sqrt_start_SI, + input logic Start_SI, + input logic Kill_SI, + input logic Special_case_SBI, + input logic Special_case_dly_SBI, + input logic [C_PC-1:0] Precision_ctl_SI, + input logic [1:0] Format_sel_SI, + input logic [C_MANT_FP64:0] Numerator_DI, + input logic [C_EXP_FP64:0] Exp_num_DI, + input logic [C_MANT_FP64:0] Denominator_DI, + input logic [C_EXP_FP64:0] Exp_den_DI, + + + output logic Div_start_dly_SO , + output logic Sqrt_start_dly_SO, + output logic Div_enable_SO, + output logic Sqrt_enable_SO, + + + //To next stage + output logic Full_precision_SO, + output logic FP32_SO, + output logic FP64_SO, + output logic FP16_SO, + output logic FP16ALT_SO, + + output logic Ready_SO, + output logic Done_SO, + + output logic [C_MANT_FP64+4:0] Mant_result_prenorm_DO, + // output logic [3:0] Round_bit_DO, + output logic [C_EXP_FP64+1:0] Exp_result_prenorm_DO + ); + + logic [C_MANT_FP64+1+4:0] Partial_remainder_DN,Partial_remainder_DP; //58bits,r=q+2 + logic [C_MANT_FP64+4:0] Quotient_DP; //57bits + ///////////////////////////////////////////////////////////////////////////// + // Assign Inputs // + ///////////////////////////////////////////////////////////////////////////// + logic [C_MANT_FP64+1:0] Numerator_se_D; //sign extension and hidden bit + logic [C_MANT_FP64+1:0] Denominator_se_D; //signa extension and hidden bit + logic [C_MANT_FP64+1:0] Denominator_se_DB; //1's complement + + assign Numerator_se_D={1'b0,Numerator_DI}; + + assign Denominator_se_D={1'b0,Denominator_DI}; + + always_comb + begin + if(FP32_SO) + begin + Denominator_se_DB={~Denominator_se_D[C_MANT_FP64+1:C_MANT_FP64-C_MANT_FP32], {(C_MANT_FP64-C_MANT_FP32){1'b0}} }; + end + else if(FP64_SO) begin + Denominator_se_DB=~Denominator_se_D; + end + else if(FP16_SO) begin + Denominator_se_DB={~Denominator_se_D[C_MANT_FP64+1:C_MANT_FP64-C_MANT_FP16], {(C_MANT_FP64-C_MANT_FP16){1'b0}} }; + end + else begin + Denominator_se_DB={~Denominator_se_D[C_MANT_FP64+1:C_MANT_FP64-C_MANT_FP16ALT], {(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; + end + end + + + logic [C_MANT_FP64+1:0] Mant_D_sqrt_Norm; + + assign Mant_D_sqrt_Norm=Exp_num_DI[0]?{1'b0,Numerator_DI}:{Numerator_DI,1'b0}; //for sqrt + + ///////////////////////////////////////////////////////////////////////////// + // Format Selection // + ///////////////////////////////////////////////////////////////////////////// + logic [1:0] Format_sel_S; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Format_sel_S<='b0; + end + else if(Start_SI&&Ready_SO) + begin + Format_sel_S<=Format_sel_SI; + end + else + begin + Format_sel_S<=Format_sel_S; + end + end + + assign FP32_SO = (Format_sel_S==2'b00); + assign FP64_SO = (Format_sel_S==2'b01); + assign FP16_SO = (Format_sel_S==2'b10); + assign FP16ALT_SO = (Format_sel_S==2'b11); + + + + ///////////////////////////////////////////////////////////////////////////// + // Precision Control // + ///////////////////////////////////////////////////////////////////////////// + + logic [C_PC-1:0] Precision_ctl_S; + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Precision_ctl_S<='b0; + end + else if(Start_SI&&Ready_SO) + begin + Precision_ctl_S<=Precision_ctl_SI; + end + else + begin + Precision_ctl_S<=Precision_ctl_S; + end + end + assign Full_precision_SO = (Precision_ctl_S==6'h00); + + + + logic [5:0] State_ctl_S; + logic [5:0] State_Two_iteration_unit_S; + logic [5:0] State_Four_iteration_unit_S; + + assign State_Two_iteration_unit_S = Precision_ctl_S[C_PC-1:1]; //Two iteration units + assign State_Four_iteration_unit_S = Precision_ctl_S[C_PC-1:2]; //Four iteration units + always_comb + begin + case(Iteration_unit_num_S) +//////////////////////one iteration unit, start/////////////////////////////////////// + 2'b00: //one iteration unit + begin + case(Format_sel_S) + 2'b00: //FP32 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h1b; //24+4 more iterations for rounding bits + end + else + begin + State_ctl_S = Precision_ctl_S; + end + end + 2'b01: //FP64 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h38; //53+4 more iterations for rounding bits + end + else + begin + State_ctl_S = Precision_ctl_S; + end + end + 2'b10: //FP16 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h0e; //11+4 more iterations for rounding bits + end + else + begin + State_ctl_S = Precision_ctl_S; + end + end + 2'b11: //FP16ALT + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h0b; //8+4 more iterations for rounding bits + end + else + begin + State_ctl_S = Precision_ctl_S; + end + end + endcase + end +//////////////////////one iteration unit, end/////////////////////////////////////// + +//////////////////////two iteration units, start/////////////////////////////////////// + 2'b01: //two iteration units + begin + case(Format_sel_S) + 2'b00: //FP32 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h0d; //24+4 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Two_iteration_unit_S; + end + end + 2'b01: //FP64 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h1b; //53+3 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Two_iteration_unit_S; + end + end + 2'b10: //FP16 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h06; //11+3 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Two_iteration_unit_S; + end + end + 2'b11: //FP16ALT + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h05; //8+4 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Two_iteration_unit_S; + end + end + endcase + end +//////////////////////two iteration units, end/////////////////////////////////////// + +//////////////////////three iteration units, start/////////////////////////////////////// + 2'b10: //three iteration units + begin + case(Format_sel_S) + 2'b00: //FP32 + begin + case(Precision_ctl_S) + 6'h00: + begin + State_ctl_S = 6'h08; //24+3 more iterations for rounding bits + end + 6'h06,6'h07,6'h08: + begin + State_ctl_S = 6'h02; + end + 6'h09,6'h0a,6'h0b: + begin + State_ctl_S = 6'h03; + end + 6'h0c,6'h0d,6'h0e: + begin + State_ctl_S = 6'h04; + end + 6'h0f,6'h10,6'h11: + begin + State_ctl_S = 6'h05; + end + 6'h12,6'h13,6'h14: + begin + State_ctl_S = 6'h06; + end + 6'h15,6'h16,6'h17: + begin + State_ctl_S = 6'h07; + end + default: + begin + State_ctl_S = 6'h08; //24+3 more iterations for rounding bits + end + endcase + end + 2'b01: //FP64 + begin + case(Precision_ctl_S) + 6'h00: + begin + State_ctl_S = 6'h12; //53+4 more iterations for rounding bits + end + 6'h06,6'h07,6'h08: + begin + State_ctl_S = 6'h02; + end + 6'h09,6'h0a,6'h0b: + begin + State_ctl_S = 6'h03; + end + 6'h0c,6'h0d,6'h0e: + begin + State_ctl_S = 6'h04; + end + 6'h0f,6'h10,6'h11: + begin + State_ctl_S = 6'h05; + end + 6'h12,6'h13,6'h14: + begin + State_ctl_S = 6'h06; + end + 6'h15,6'h16,6'h17: + begin + State_ctl_S = 6'h07; + end + 6'h18,6'h19,6'h1a: + begin + State_ctl_S = 6'h08; + end + 6'h1b,6'h1c,6'h1d: + begin + State_ctl_S = 6'h09; + end + 6'h1e,6'h1f,6'h20: + begin + State_ctl_S = 6'h0a; + end + 6'h21,6'h22,6'h23: + begin + State_ctl_S = 6'h0b; + end + 6'h24,6'h25,6'h26: + begin + State_ctl_S = 6'h0c; + end + 6'h27,6'h28,6'h29: + begin + State_ctl_S = 6'h0d; + end + 6'h2a,6'h2b,6'h2c: + begin + State_ctl_S = 6'h0e; + end + 6'h2d,6'h2e,6'h2f: + begin + State_ctl_S = 6'h0f; + end + 6'h30,6'h31,6'h32: + begin + State_ctl_S = 6'h10; + end + 6'h33,6'h34,6'h35: + begin + State_ctl_S = 6'h11; + end + default: + begin + State_ctl_S = 6'h12; //53+4 more iterations for rounding bits + end + endcase + end + 2'b10: //FP16 + begin + case(Precision_ctl_S) + 6'h00: + begin + State_ctl_S = 6'h04; //12+3 more iterations for rounding bits + end + 6'h06,6'h07,6'h08: + begin + State_ctl_S = 6'h02; + end + 6'h09,6'h0a,6'h0b: + begin + State_ctl_S = 6'h03; + end + default: + begin + State_ctl_S = 6'h04; //12+3 more iterations for rounding bits + end + endcase + end + 2'b11: //FP16ALT + begin + case(Precision_ctl_S) + 6'h00: + begin + State_ctl_S = 6'h03; //8+4 more iterations for rounding bits + end + 6'h06,6'h07,6'h08: + begin + State_ctl_S = 6'h02; + end + default: + begin + State_ctl_S = 6'h03; //8+4 more iterations for rounding bits + end + endcase + end + endcase + end +//////////////////////three iteration units, end/////////////////////////////////////// + +//////////////////////four iteration units, start/////////////////////////////////////// + 2'b11: //four iteration units + begin + case(Format_sel_S) + 2'b00: //FP32 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h06; //24+4 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Four_iteration_unit_S; + end + end + 2'b01: //FP64 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h0d; //53+3 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Four_iteration_unit_S; + end + end + 2'b10: //FP16 + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h03; //11+4 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Four_iteration_unit_S; + end + end + 2'b11: //FP16ALT + begin + if(Full_precision_SO) + begin + State_ctl_S = 6'h02; //8+4 more iterations for rounding bits + end + else + begin + State_ctl_S = State_Four_iteration_unit_S; + end + end + endcase + end +//////////////////////four iteration units, end/////////////////////////////////////// + + endcase + end + + + ///////////////////////////////////////////////////////////////////////////// + // control logic // + ///////////////////////////////////////////////////////////////////////////// + + logic Div_start_dly_S; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) // generate Div_start_dly_S signal + begin + if(~Rst_RBI) + begin + Div_start_dly_S<=1'b0; + end + else if(Div_start_SI&&Ready_SO) + begin + Div_start_dly_S<=1'b1; + end + else + begin + Div_start_dly_S<=1'b0; + end + end + + assign Div_start_dly_SO=Div_start_dly_S; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) begin // generate Div_enable_SO signal + if(~Rst_RBI) + Div_enable_SO<=1'b0; + // Synchronous rst_ni with Flush + else if (Kill_SI) + Div_enable_SO <= 1'b0; + else if(Div_start_SI&&Ready_SO) + Div_enable_SO<=1'b1; + else if(Done_SO) + Div_enable_SO<=1'b0; + else + Div_enable_SO<=Div_enable_SO; + end + + logic Sqrt_start_dly_S; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) // generate Sqrt_start_dly_SI signal + begin + if(~Rst_RBI) + begin + Sqrt_start_dly_S<=1'b0; + end + else if(Sqrt_start_SI&&Ready_SO) + begin + Sqrt_start_dly_S<=1'b1; + end + else + begin + Sqrt_start_dly_S<=1'b0; + end + end + assign Sqrt_start_dly_SO=Sqrt_start_dly_S; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) begin // generate Sqrt_enable_SO signal + if(~Rst_RBI) + Sqrt_enable_SO<=1'b0; + else if (Kill_SI) + Sqrt_enable_SO <= 1'b0; + else if(Sqrt_start_SI&&Ready_SO) + Sqrt_enable_SO<=1'b1; + else if(Done_SO) + Sqrt_enable_SO<=1'b0; + else + Sqrt_enable_SO<=Sqrt_enable_SO; + end + + logic [5:0] Crtl_cnt_S; + logic Start_dly_S; + + assign Start_dly_S=Div_start_dly_S |Sqrt_start_dly_S; + + logic Fsm_enable_S; + assign Fsm_enable_S=( (Start_dly_S | (| Crtl_cnt_S)) && (~Kill_SI) && Special_case_dly_SBI); + + logic Final_state_S; + assign Final_state_S= (Crtl_cnt_S==State_ctl_S); + + + always_ff @(posedge Clk_CI, negedge Rst_RBI) //control_FSM + begin + if (~Rst_RBI) + begin + Crtl_cnt_S <= '0; + end + else if (Final_state_S | Kill_SI) + begin + Crtl_cnt_S <= '0; + end + else if(Fsm_enable_S) // one cycle Start_SI + begin + Crtl_cnt_S <= Crtl_cnt_S+1; + end + else + begin + Crtl_cnt_S <= '0; + end + end // always_ff + + + + always_ff @(posedge Clk_CI, negedge Rst_RBI) //Generate Done_SO, they can share this Done_SO. + begin + if(~Rst_RBI) + begin + Done_SO<=1'b0; + end + else if(Start_SI&&Ready_SO) + begin + if(~Special_case_SBI) + begin + Done_SO<=1'b1; + end + else + begin + Done_SO<=1'b0; + end + end + else if(Final_state_S) + begin + Done_SO<=1'b1; + end + else + begin + Done_SO<=1'b0; + end + end + + + + + always_ff @(posedge Clk_CI, negedge Rst_RBI) //Generate Ready_SO + begin + if(~Rst_RBI) + begin + Ready_SO<=1'b1; + end + + else if(Start_SI&&Ready_SO) + begin + if(~Special_case_SBI) + begin + Ready_SO<=1'b1; + end + else + begin + Ready_SO<=1'b0; + end + end + else if(Final_state_S | Kill_SI) + begin + Ready_SO<=1'b1; + end + else + begin + Ready_SO<=Ready_SO; + end + end + + + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b00, start // + //////////////////////////////////////////////////////////////////////////// + + logic Qcnt_one_0; + logic Qcnt_one_1; + logic [1:0] Qcnt_one_2; + logic [2:0] Qcnt_one_3; + logic [3:0] Qcnt_one_4; + logic [4:0] Qcnt_one_5; + logic [5:0] Qcnt_one_6; + logic [6:0] Qcnt_one_7; + logic [7:0] Qcnt_one_8; + logic [8:0] Qcnt_one_9; + logic [9:0] Qcnt_one_10; + logic [10:0] Qcnt_one_11; + logic [11:0] Qcnt_one_12; + logic [12:0] Qcnt_one_13; + logic [13:0] Qcnt_one_14; + logic [14:0] Qcnt_one_15; + logic [15:0] Qcnt_one_16; + logic [16:0] Qcnt_one_17; + logic [17:0] Qcnt_one_18; + logic [18:0] Qcnt_one_19; + logic [19:0] Qcnt_one_20; + logic [20:0] Qcnt_one_21; + logic [21:0] Qcnt_one_22; + logic [22:0] Qcnt_one_23; + logic [23:0] Qcnt_one_24; + logic [24:0] Qcnt_one_25; + logic [25:0] Qcnt_one_26; + logic [26:0] Qcnt_one_27; + logic [27:0] Qcnt_one_28; + logic [28:0] Qcnt_one_29; + logic [29:0] Qcnt_one_30; + logic [30:0] Qcnt_one_31; + logic [31:0] Qcnt_one_32; + logic [32:0] Qcnt_one_33; + logic [33:0] Qcnt_one_34; + logic [34:0] Qcnt_one_35; + logic [35:0] Qcnt_one_36; + logic [36:0] Qcnt_one_37; + logic [37:0] Qcnt_one_38; + logic [38:0] Qcnt_one_39; + logic [39:0] Qcnt_one_40; + logic [40:0] Qcnt_one_41; + logic [41:0] Qcnt_one_42; + logic [42:0] Qcnt_one_43; + logic [43:0] Qcnt_one_44; + logic [44:0] Qcnt_one_45; + logic [45:0] Qcnt_one_46; + logic [46:0] Qcnt_one_47; + logic [47:0] Qcnt_one_48; + logic [48:0] Qcnt_one_49; + logic [49:0] Qcnt_one_50; + logic [50:0] Qcnt_one_51; + logic [51:0] Qcnt_one_52; + logic [52:0] Qcnt_one_53; + logic [53:0] Qcnt_one_54; + logic [54:0] Qcnt_one_55; + logic [55:0] Qcnt_one_56; + logic [56:0] Qcnt_one_57; + logic [57:0] Qcnt_one_58; + logic [58:0] Qcnt_one_59; + logic [59:0] Qcnt_one_60; + + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b00, end // + //////////////////////////////////////////////////////////////////////////// + + + + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b01, start // + //////////////////////////////////////////////////////////////////////////// + logic [1:0] Qcnt_two_0; + logic [2:0] Qcnt_two_1; + logic [4:0] Qcnt_two_2; + logic [6:0] Qcnt_two_3; + logic [8:0] Qcnt_two_4; + logic [10:0] Qcnt_two_5; + logic [12:0] Qcnt_two_6; + logic [14:0] Qcnt_two_7; + logic [16:0] Qcnt_two_8; + logic [18:0] Qcnt_two_9; + logic [20:0] Qcnt_two_10; + logic [22:0] Qcnt_two_11; + logic [24:0] Qcnt_two_12; + logic [26:0] Qcnt_two_13; + logic [28:0] Qcnt_two_14; + logic [30:0] Qcnt_two_15; + logic [32:0] Qcnt_two_16; + logic [34:0] Qcnt_two_17; + logic [36:0] Qcnt_two_18; + logic [38:0] Qcnt_two_19; + logic [40:0] Qcnt_two_20; + logic [42:0] Qcnt_two_21; + logic [44:0] Qcnt_two_22; + logic [46:0] Qcnt_two_23; + logic [48:0] Qcnt_two_24; + logic [50:0] Qcnt_two_25; + logic [52:0] Qcnt_two_26; + logic [54:0] Qcnt_two_27; + logic [56:0] Qcnt_two_28; + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b01, end // + //////////////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b10, start // + //////////////////////////////////////////////////////////////////////////// + logic [2:0] Qcnt_three_0; + logic [4:0] Qcnt_three_1; + logic [7:0] Qcnt_three_2; + logic [10:0] Qcnt_three_3; + logic [13:0] Qcnt_three_4; + logic [16:0] Qcnt_three_5; + logic [19:0] Qcnt_three_6; + logic [22:0] Qcnt_three_7; + logic [25:0] Qcnt_three_8; + logic [28:0] Qcnt_three_9; + logic [31:0] Qcnt_three_10; + logic [34:0] Qcnt_three_11; + logic [37:0] Qcnt_three_12; + logic [40:0] Qcnt_three_13; + logic [43:0] Qcnt_three_14; + logic [46:0] Qcnt_three_15; + logic [49:0] Qcnt_three_16; + logic [52:0] Qcnt_three_17; + logic [55:0] Qcnt_three_18; + logic [58:0] Qcnt_three_19; + logic [61:0] Qcnt_three_20; + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b10, end // + //////////////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b11, start // + //////////////////////////////////////////////////////////////////////////// + logic [3:0] Qcnt_four_0; + logic [6:0] Qcnt_four_1; + logic [10:0] Qcnt_four_2; + logic [14:0] Qcnt_four_3; + logic [18:0] Qcnt_four_4; + logic [22:0] Qcnt_four_5; + logic [26:0] Qcnt_four_6; + logic [30:0] Qcnt_four_7; + logic [34:0] Qcnt_four_8; + logic [38:0] Qcnt_four_9; + logic [42:0] Qcnt_four_10; + logic [46:0] Qcnt_four_11; + logic [50:0] Qcnt_four_12; + logic [54:0] Qcnt_four_13; + logic [58:0] Qcnt_four_14; + + ///////////////////////////////////////////////////////////////////////////// + // Declarations for square root when Iteration_unit_num_S = 2'b11, end // + //////////////////////////////////////////////////////////////////////////// + + + + logic [C_MANT_FP64+1+4:0] Sqrt_R0,Sqrt_Q0,Q_sqrt0,Q_sqrt_com_0; + logic [C_MANT_FP64+1+4:0] Sqrt_R1,Sqrt_Q1,Q_sqrt1,Q_sqrt_com_1; + logic [C_MANT_FP64+1+4:0] Sqrt_R2,Sqrt_Q2,Q_sqrt2,Q_sqrt_com_2; + logic [C_MANT_FP64+1+4:0] Sqrt_R3,Sqrt_Q3,Q_sqrt3,Q_sqrt_com_3,Sqrt_R4; //Sqrt_Q4; + + + logic [1:0] Sqrt_DI [3:0]; + logic [1:0] Sqrt_DO [3:0]; + logic Sqrt_carry_DO; + + + logic [C_MANT_FP64+1+4:0] Iteration_cell_a_D [3:0]; + logic [C_MANT_FP64+1+4:0] Iteration_cell_b_D [3:0]; + logic [C_MANT_FP64+1+4:0] Iteration_cell_a_BMASK_D [3:0]; + logic [C_MANT_FP64+1+4:0] Iteration_cell_b_BMASK_D [3:0]; + logic Iteration_cell_carry_D [3:0]; + logic [C_MANT_FP64+1+4:0] Iteration_cell_sum_D [3:0]; + logic [C_MANT_FP64+1+4:0] Iteration_cell_sum_AMASK_D [3:0]; + + + logic [3:0] Sqrt_quotinent_S; + + + always_comb + begin // + case (Format_sel_S) + 2'b00: + begin + Sqrt_quotinent_S = {(~Iteration_cell_sum_AMASK_D[0][C_MANT_FP32+5]),(~Iteration_cell_sum_AMASK_D[1][C_MANT_FP32+5]),(~Iteration_cell_sum_AMASK_D[2][C_MANT_FP32+5]),(~Iteration_cell_sum_AMASK_D[3][C_MANT_FP32+5])}; + Q_sqrt_com_0 ={ {(C_MANT_FP64-C_MANT_FP32){1'b0}},~Q_sqrt0[C_MANT_FP32+5:0] }; + Q_sqrt_com_1 ={ {(C_MANT_FP64-C_MANT_FP32){1'b0}},~Q_sqrt1[C_MANT_FP32+5:0] }; + Q_sqrt_com_2 ={ {(C_MANT_FP64-C_MANT_FP32){1'b0}},~Q_sqrt2[C_MANT_FP32+5:0] }; + Q_sqrt_com_3 ={ {(C_MANT_FP64-C_MANT_FP32){1'b0}},~Q_sqrt3[C_MANT_FP32+5:0] }; + end + 2'b01: + begin + Sqrt_quotinent_S = {Iteration_cell_carry_D[0],Iteration_cell_carry_D[1],Iteration_cell_carry_D[2],Iteration_cell_carry_D[3]}; + Q_sqrt_com_0=~Q_sqrt0; + Q_sqrt_com_1=~Q_sqrt1; + Q_sqrt_com_2=~Q_sqrt2; + Q_sqrt_com_3=~Q_sqrt3; + end + 2'b10: + begin + Sqrt_quotinent_S = {(~Iteration_cell_sum_AMASK_D[0][C_MANT_FP16+5]),(~Iteration_cell_sum_AMASK_D[1][C_MANT_FP16+5]),(~Iteration_cell_sum_AMASK_D[2][C_MANT_FP16+5]),(~Iteration_cell_sum_AMASK_D[3][C_MANT_FP16+5])}; + Q_sqrt_com_0 ={ {(C_MANT_FP64-C_MANT_FP16){1'b0}},~Q_sqrt0[C_MANT_FP16+5:0] }; + Q_sqrt_com_1 ={ {(C_MANT_FP64-C_MANT_FP16){1'b0}},~Q_sqrt1[C_MANT_FP16+5:0] }; + Q_sqrt_com_2 ={ {(C_MANT_FP64-C_MANT_FP16){1'b0}},~Q_sqrt2[C_MANT_FP16+5:0] }; + Q_sqrt_com_3 ={ {(C_MANT_FP64-C_MANT_FP16){1'b0}},~Q_sqrt3[C_MANT_FP16+5:0] }; + end + 2'b11: + begin + Sqrt_quotinent_S = {(~Iteration_cell_sum_AMASK_D[0][C_MANT_FP16ALT+5]),(~Iteration_cell_sum_AMASK_D[1][C_MANT_FP16ALT+5]),(~Iteration_cell_sum_AMASK_D[2][C_MANT_FP16ALT+5]),(~Iteration_cell_sum_AMASK_D[3][C_MANT_FP16ALT+5])}; + Q_sqrt_com_0 ={ {(C_MANT_FP64-C_MANT_FP16ALT){1'b0}},~Q_sqrt0[C_MANT_FP16ALT+5:0] }; + Q_sqrt_com_1 ={ {(C_MANT_FP64-C_MANT_FP16ALT){1'b0}},~Q_sqrt1[C_MANT_FP16ALT+5:0] }; + Q_sqrt_com_2 ={ {(C_MANT_FP64-C_MANT_FP16ALT){1'b0}},~Q_sqrt2[C_MANT_FP16ALT+5:0] }; + Q_sqrt_com_3 ={ {(C_MANT_FP64-C_MANT_FP16ALT){1'b0}},~Q_sqrt3[C_MANT_FP16ALT+5:0] }; + end + endcase + end + + + + assign Qcnt_one_0= {1'b0}; //qk for each feedback + assign Qcnt_one_1= {Quotient_DP[0]}; + assign Qcnt_one_2= {Quotient_DP[1:0]}; + assign Qcnt_one_3= {Quotient_DP[2:0]}; + assign Qcnt_one_4= {Quotient_DP[3:0]}; + assign Qcnt_one_5= {Quotient_DP[4:0]}; + assign Qcnt_one_6= {Quotient_DP[5:0]}; + assign Qcnt_one_7= {Quotient_DP[6:0]}; + assign Qcnt_one_8= {Quotient_DP[7:0]}; + assign Qcnt_one_9= {Quotient_DP[8:0]}; + assign Qcnt_one_10= {Quotient_DP[9:0]}; + assign Qcnt_one_11= {Quotient_DP[10:0]}; + assign Qcnt_one_12= {Quotient_DP[11:0]}; + assign Qcnt_one_13= {Quotient_DP[12:0]}; + assign Qcnt_one_14= {Quotient_DP[13:0]}; + assign Qcnt_one_15= {Quotient_DP[14:0]}; + assign Qcnt_one_16= {Quotient_DP[15:0]}; + assign Qcnt_one_17= {Quotient_DP[16:0]}; + assign Qcnt_one_18= {Quotient_DP[17:0]}; + assign Qcnt_one_19= {Quotient_DP[18:0]}; + assign Qcnt_one_20= {Quotient_DP[19:0]}; + assign Qcnt_one_21= {Quotient_DP[20:0]}; + assign Qcnt_one_22= {Quotient_DP[21:0]}; + assign Qcnt_one_23= {Quotient_DP[22:0]}; + assign Qcnt_one_24= {Quotient_DP[23:0]}; + assign Qcnt_one_25= {Quotient_DP[24:0]}; + assign Qcnt_one_26= {Quotient_DP[25:0]}; + assign Qcnt_one_27= {Quotient_DP[26:0]}; + assign Qcnt_one_28= {Quotient_DP[27:0]}; + assign Qcnt_one_29= {Quotient_DP[28:0]}; + assign Qcnt_one_30= {Quotient_DP[29:0]}; + assign Qcnt_one_31= {Quotient_DP[30:0]}; + assign Qcnt_one_32= {Quotient_DP[31:0]}; + assign Qcnt_one_33= {Quotient_DP[32:0]}; + assign Qcnt_one_34= {Quotient_DP[33:0]}; + assign Qcnt_one_35= {Quotient_DP[34:0]}; + assign Qcnt_one_36= {Quotient_DP[35:0]}; + assign Qcnt_one_37= {Quotient_DP[36:0]}; + assign Qcnt_one_38= {Quotient_DP[37:0]}; + assign Qcnt_one_39= {Quotient_DP[38:0]}; + assign Qcnt_one_40= {Quotient_DP[39:0]}; + assign Qcnt_one_41= {Quotient_DP[40:0]}; + assign Qcnt_one_42= {Quotient_DP[41:0]}; + assign Qcnt_one_43= {Quotient_DP[42:0]}; + assign Qcnt_one_44= {Quotient_DP[43:0]}; + assign Qcnt_one_45= {Quotient_DP[44:0]}; + assign Qcnt_one_46= {Quotient_DP[45:0]}; + assign Qcnt_one_47= {Quotient_DP[46:0]}; + assign Qcnt_one_48= {Quotient_DP[47:0]}; + assign Qcnt_one_49= {Quotient_DP[48:0]}; + assign Qcnt_one_50= {Quotient_DP[49:0]}; + assign Qcnt_one_51= {Quotient_DP[50:0]}; + assign Qcnt_one_52= {Quotient_DP[51:0]}; + assign Qcnt_one_53= {Quotient_DP[52:0]}; + assign Qcnt_one_54= {Quotient_DP[53:0]}; + assign Qcnt_one_55= {Quotient_DP[54:0]}; + assign Qcnt_one_56= {Quotient_DP[55:0]}; + assign Qcnt_one_57= {Quotient_DP[56:0]}; + + + assign Qcnt_two_0 = {1'b0, Sqrt_quotinent_S[3]}; //qk for each feedback + assign Qcnt_two_1 = {Quotient_DP[1:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_2 = {Quotient_DP[3:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_3 = {Quotient_DP[5:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_4 = {Quotient_DP[7:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_5 = {Quotient_DP[9:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_6 = {Quotient_DP[11:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_7 = {Quotient_DP[13:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_8 = {Quotient_DP[15:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_9 = {Quotient_DP[17:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_10 = {Quotient_DP[19:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_11 = {Quotient_DP[21:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_12 = {Quotient_DP[23:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_13 = {Quotient_DP[25:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_14 = {Quotient_DP[27:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_15 = {Quotient_DP[29:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_16 = {Quotient_DP[31:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_17 = {Quotient_DP[33:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_18 = {Quotient_DP[35:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_19 = {Quotient_DP[37:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_20 = {Quotient_DP[39:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_21 = {Quotient_DP[41:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_22 = {Quotient_DP[43:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_23 = {Quotient_DP[45:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_24 = {Quotient_DP[47:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_25 = {Quotient_DP[49:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_26 = {Quotient_DP[51:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_27 = {Quotient_DP[53:0],Sqrt_quotinent_S[3]}; + assign Qcnt_two_28 = {Quotient_DP[55:0],Sqrt_quotinent_S[3]}; + + + assign Qcnt_three_0 = {1'b0, Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; //qk for each feedback + assign Qcnt_three_1 = {Quotient_DP[2:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_2 = {Quotient_DP[5:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_3 = {Quotient_DP[8:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_4 = {Quotient_DP[11:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_5 = {Quotient_DP[14:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_6 = {Quotient_DP[17:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_7 = {Quotient_DP[20:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_8 = {Quotient_DP[23:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_9 = {Quotient_DP[26:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_10 = {Quotient_DP[29:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_11 = {Quotient_DP[32:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_12 = {Quotient_DP[35:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_13 = {Quotient_DP[38:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_14 = {Quotient_DP[41:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_15 = {Quotient_DP[44:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_16 = {Quotient_DP[47:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_17 = {Quotient_DP[50:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_18 = {Quotient_DP[53:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + assign Qcnt_three_19 = {Quotient_DP[56:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2]}; + + + assign Qcnt_four_0 = {1'b0, Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_1 = {Quotient_DP[3:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_2 = {Quotient_DP[7:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_3 = {Quotient_DP[11:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_4 = {Quotient_DP[15:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_5 = {Quotient_DP[19:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_6 = {Quotient_DP[23:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_7 = {Quotient_DP[27:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_8 = {Quotient_DP[31:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_9 = {Quotient_DP[35:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_10 = {Quotient_DP[39:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_11 = {Quotient_DP[43:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_12 = {Quotient_DP[47:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_13 = {Quotient_DP[51:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + assign Qcnt_four_14 = {Quotient_DP[55:0],Sqrt_quotinent_S[3],Sqrt_quotinent_S[2],Sqrt_quotinent_S[1]}; + + + + + always_comb begin // the intermediate operands for sqrt + + case(Iteration_unit_num_S) + 2'b00: + begin + + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b00, start // + ///////////////////////////////////////////////////////////////////////////// + + + + + case(Crtl_cnt_S) + + 6'b000000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_one_0}; + Sqrt_Q0=Q_sqrt_com_0; + end + 6'b000001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_one_1}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b000010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-3:C_MANT_FP64-4]; + Q_sqrt0={{(C_MANT_FP64+4){1'b0}},Qcnt_one_2}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b000011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-5:C_MANT_FP64-6]; + Q_sqrt0={{(C_MANT_FP64+3){1'b0}},Qcnt_one_3}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b000100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-7:C_MANT_FP64-8]; + Q_sqrt0={{(C_MANT_FP64+2){1'b0}},Qcnt_one_4}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b000101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-9:C_MANT_FP64-10]; + Q_sqrt0={{(C_MANT_FP64+1){1'b0}},Qcnt_one_5}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b000110: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-11:C_MANT_FP64-12]; + Q_sqrt0={{(C_MANT_FP64){1'b0}},Qcnt_one_6}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b000111: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-13:C_MANT_FP64-14]; + Q_sqrt0={{(C_MANT_FP64-1){1'b0}},Qcnt_one_7}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-15:C_MANT_FP64-16]; + Q_sqrt0={{(C_MANT_FP64-2){1'b0}},Qcnt_one_8}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-17:C_MANT_FP64-18]; + Q_sqrt0={{(C_MANT_FP64-3){1'b0}},Qcnt_one_9}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-19:C_MANT_FP64-20]; + Q_sqrt0={{(C_MANT_FP64-4){1'b0}},Qcnt_one_10}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-21:C_MANT_FP64-22]; + Q_sqrt0={{(C_MANT_FP64-5){1'b0}},Qcnt_one_11}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-23:C_MANT_FP64-24]; + Q_sqrt0={{(C_MANT_FP64-6){1'b0}},Qcnt_one_12}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-25:C_MANT_FP64-26]; + Q_sqrt0={{(C_MANT_FP64-7){1'b0}},Qcnt_one_13}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001110: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-27:C_MANT_FP64-28]; + Q_sqrt0={{(C_MANT_FP64-8){1'b0}},Qcnt_one_14}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b001111: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-29:C_MANT_FP64-30]; + Q_sqrt0={{(C_MANT_FP64-9){1'b0}},Qcnt_one_15}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-31:C_MANT_FP64-32]; + Q_sqrt0={{(C_MANT_FP64-10){1'b0}},Qcnt_one_16}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-33:C_MANT_FP64-34]; + Q_sqrt0={{(C_MANT_FP64-11){1'b0}},Qcnt_one_17}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-35:C_MANT_FP64-36]; + Q_sqrt0={{(C_MANT_FP64-12){1'b0}},Qcnt_one_18}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-37:C_MANT_FP64-38]; + Q_sqrt0={{(C_MANT_FP64-13){1'b0}},Qcnt_one_19}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-39:C_MANT_FP64-40]; + Q_sqrt0={{(C_MANT_FP64-14){1'b0}},Qcnt_one_20}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-41:C_MANT_FP64-42]; + Q_sqrt0={{(C_MANT_FP64-15){1'b0}},Qcnt_one_21}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010110: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-43:C_MANT_FP64-44]; + Q_sqrt0={{(C_MANT_FP64-16){1'b0}},Qcnt_one_22}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b010111: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-45:C_MANT_FP64-46]; + Q_sqrt0={{(C_MANT_FP64-17){1'b0}},Qcnt_one_23}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-47:C_MANT_FP64-48]; + Q_sqrt0={{(C_MANT_FP64-18){1'b0}},Qcnt_one_24}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-49:C_MANT_FP64-50]; + Q_sqrt0={{(C_MANT_FP64-19){1'b0}},Qcnt_one_25}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-51:C_MANT_FP64-52]; + Q_sqrt0={{(C_MANT_FP64-20){1'b0}},Qcnt_one_26}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-21){1'b0}},Qcnt_one_27}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-22){1'b0}},Qcnt_one_28}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-23){1'b0}},Qcnt_one_29}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-24){1'b0}},Qcnt_one_30}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b011111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-25){1'b0}},Qcnt_one_31}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-26){1'b0}},Qcnt_one_32}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-27){1'b0}},Qcnt_one_33}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-28){1'b0}},Qcnt_one_34}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-29){1'b0}},Qcnt_one_35}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-30){1'b0}},Qcnt_one_36}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-31){1'b0}},Qcnt_one_37}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-32){1'b0}},Qcnt_one_38}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b100111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-33){1'b0}},Qcnt_one_39}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-34){1'b0}},Qcnt_one_40}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-35){1'b0}},Qcnt_one_41}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-36){1'b0}},Qcnt_one_42}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-37){1'b0}},Qcnt_one_43}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-38){1'b0}},Qcnt_one_44}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-39){1'b0}},Qcnt_one_45}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-40){1'b0}},Qcnt_one_46}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b101111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-41){1'b0}},Qcnt_one_47}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-42){1'b0}},Qcnt_one_48}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-43){1'b0}},Qcnt_one_49}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-44){1'b0}},Qcnt_one_50}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-45){1'b0}},Qcnt_one_51}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-46){1'b0}},Qcnt_one_52}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-47){1'b0}},Qcnt_one_53}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-48){1'b0}},Qcnt_one_54}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b110111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-49){1'b0}},Qcnt_one_55}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + 6'b111000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-50){1'b0}},Qcnt_one_56}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + end + + default: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0='0; + Sqrt_Q0='0; + end + endcase + end + + + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b00, end // + ///////////////////////////////////////////////////////////////////////////// + + + 2'b01: + begin + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b01, start // + ///////////////////////////////////////////////////////////////////////////// + case(Crtl_cnt_S) + + 6'b000000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_two_0[1]}; + Sqrt_Q0=Q_sqrt_com_0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt1={{(C_MANT_FP64+4){1'b0}},Qcnt_two_0[1:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-3:C_MANT_FP64-4]; + Q_sqrt0={{(C_MANT_FP64+4){1'b0}},Qcnt_two_1[2:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-5:C_MANT_FP64-6]; + Q_sqrt1={{(C_MANT_FP64+3){1'b0}},Qcnt_two_1[2:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-7:C_MANT_FP64-8]; + Q_sqrt0={{(C_MANT_FP64+2){1'b0}},Qcnt_two_2[4:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-9:C_MANT_FP64-10]; + Q_sqrt1={{(C_MANT_FP64+1){1'b0}},Qcnt_two_2[4:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-11:C_MANT_FP64-12]; + Q_sqrt0={{(C_MANT_FP64){1'b0}},Qcnt_two_3[6:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-13:C_MANT_FP64-14]; + Q_sqrt1={{(C_MANT_FP64-1){1'b0}},Qcnt_two_3[6:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-15:C_MANT_FP64-16]; + Q_sqrt0={{(C_MANT_FP64-2){1'b0}},Qcnt_two_4[8:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-17:C_MANT_FP64-18]; + Q_sqrt1={{(C_MANT_FP64-3){1'b0}},Qcnt_two_4[8:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-19:C_MANT_FP64-20]; + Q_sqrt0={{(C_MANT_FP64-4){1'b0}},Qcnt_two_5[10:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-21:C_MANT_FP64-22]; + Q_sqrt1={{(C_MANT_FP64-5){1'b0}},Qcnt_two_5[10:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000110: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-23:C_MANT_FP64-24]; + Q_sqrt0={{(C_MANT_FP64-6){1'b0}},Qcnt_two_6[12:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-25:C_MANT_FP64-26]; + Q_sqrt1={{(C_MANT_FP64-7){1'b0}},Qcnt_two_6[12:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b000111: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-27:C_MANT_FP64-28]; + Q_sqrt0={{(C_MANT_FP64-8){1'b0}},Qcnt_two_7[14:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-29:C_MANT_FP64-30]; + Q_sqrt1={{(C_MANT_FP64-9){1'b0}},Qcnt_two_7[14:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-31:C_MANT_FP64-32]; + Q_sqrt0={{(C_MANT_FP64-10){1'b0}},Qcnt_two_8[16:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-33:C_MANT_FP64-34]; + Q_sqrt1={{(C_MANT_FP64-11){1'b0}},Qcnt_two_8[16:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-35:C_MANT_FP64-36]; + Q_sqrt0={{(C_MANT_FP64-12){1'b0}},Qcnt_two_9[18:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-37:C_MANT_FP64-38]; + Q_sqrt1={{(C_MANT_FP64-13){1'b0}},Qcnt_two_9[18:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-39:C_MANT_FP64-40]; + Q_sqrt0={{(C_MANT_FP64-14){1'b0}},Qcnt_two_10[20:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-41:C_MANT_FP64-42]; + Q_sqrt1={{(C_MANT_FP64-15){1'b0}},Qcnt_two_10[20:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-43:C_MANT_FP64-44]; + Q_sqrt0={{(C_MANT_FP64-16){1'b0}},Qcnt_two_11[22:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-45:C_MANT_FP64-46]; + Q_sqrt1={{(C_MANT_FP64-17){1'b0}},Qcnt_two_11[22:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-47:C_MANT_FP64-48]; + Q_sqrt0={{(C_MANT_FP64-18){1'b0}},Qcnt_two_12[24:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-49:C_MANT_FP64-50]; + Q_sqrt1={{(C_MANT_FP64-19){1'b0}},Qcnt_two_12[24:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-51:C_MANT_FP64-52]; + Q_sqrt0={{(C_MANT_FP64-20){1'b0}},Qcnt_two_13[26:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-21){1'b0}},Qcnt_two_13[26:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-22){1'b0}},Qcnt_two_14[28:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-23){1'b0}},Qcnt_two_14[28:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b001111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-24){1'b0}},Qcnt_two_15[30:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-25){1'b0}},Qcnt_two_15[30:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-26){1'b0}},Qcnt_two_16[32:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-27){1'b0}},Qcnt_two_16[32:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-28){1'b0}},Qcnt_two_17[34:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-29){1'b0}},Qcnt_two_17[34:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-30){1'b0}},Qcnt_two_18[36:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-31){1'b0}},Qcnt_two_18[36:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-32){1'b0}},Qcnt_two_19[38:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-33){1'b0}},Qcnt_two_19[38:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-34){1'b0}},Qcnt_two_20[40:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-35){1'b0}},Qcnt_two_20[40:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-36){1'b0}},Qcnt_two_21[42:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-37){1'b0}},Qcnt_two_21[42:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-38){1'b0}},Qcnt_two_22[44:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-39){1'b0}},Qcnt_two_22[44:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b010111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-40){1'b0}},Qcnt_two_23[46:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-41){1'b0}},Qcnt_two_23[46:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b011000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-42){1'b0}},Qcnt_two_24[48:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-43){1'b0}},Qcnt_two_24[48:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b011001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-44){1'b0}},Qcnt_two_25[50:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-45){1'b0}},Qcnt_two_25[50:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b011010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-46){1'b0}},Qcnt_two_26[52:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-47){1'b0}},Qcnt_two_26[52:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b011011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-48){1'b0}},Qcnt_two_27[54:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-49){1'b0}},Qcnt_two_27[54:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + 6'b011100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-50){1'b0}},Qcnt_two_28[56:1]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-51){1'b0}},Qcnt_two_28[56:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + default: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_two_0[1]}; + Sqrt_Q0=Q_sqrt_com_0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt1={{(C_MANT_FP64+4){1'b0}},Qcnt_two_0[1:0]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + end + + endcase + end + + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b01, end // + ///////////////////////////////////////////////////////////////////////////// + + + 2'b10: + begin + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b10, start // + ///////////////////////////////////////////////////////////////////////////// + + case(Crtl_cnt_S) + 6'b000000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_three_0[2]}; + Sqrt_Q0=Q_sqrt_com_0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt1={{(C_MANT_FP64+4){1'b0}},Qcnt_three_0[2:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-3:C_MANT_FP64-4]; + Q_sqrt2={{(C_MANT_FP64+3){1'b0}},Qcnt_three_0[2:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-5:C_MANT_FP64-6]; + Q_sqrt0={{(C_MANT_FP64+2){1'b0}},Qcnt_three_1[4:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-7:C_MANT_FP64-8]; + Q_sqrt1={{(C_MANT_FP64+1){1'b0}},Qcnt_three_1[4:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-9:C_MANT_FP64-10]; + Q_sqrt2={{(C_MANT_FP64){1'b0}},Qcnt_three_1[4:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-11:C_MANT_FP64-12]; + Q_sqrt0={{(C_MANT_FP64-1){1'b0}},Qcnt_three_2[7:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-13:C_MANT_FP64-14]; + Q_sqrt1={{(C_MANT_FP64-2){1'b0}},Qcnt_three_2[7:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-15:C_MANT_FP64-16]; + Q_sqrt2={{(C_MANT_FP64-3){1'b0}},Qcnt_three_2[7:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-17:C_MANT_FP64-18]; + Q_sqrt0={{(C_MANT_FP64-4){1'b0}},Qcnt_three_3[10:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-19:C_MANT_FP64-20]; + Q_sqrt1={{(C_MANT_FP64-5){1'b0}},Qcnt_three_3[10:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-21:C_MANT_FP64-22]; + Q_sqrt2={{(C_MANT_FP64-6){1'b0}},Qcnt_three_3[10:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-23:C_MANT_FP64-24]; + Q_sqrt0={{(C_MANT_FP64-7){1'b0}},Qcnt_three_4[13:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-25:C_MANT_FP64-26]; + Q_sqrt1={{(C_MANT_FP64-8){1'b0}},Qcnt_three_4[13:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-27:C_MANT_FP64-28]; + Q_sqrt2={{(C_MANT_FP64-9){1'b0}},Qcnt_three_4[13:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-29:C_MANT_FP64-30]; + Q_sqrt0={{(C_MANT_FP64-10){1'b0}},Qcnt_three_5[16:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-31:C_MANT_FP64-32]; + Q_sqrt1={{(C_MANT_FP64-11){1'b0}},Qcnt_three_5[16:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-33:C_MANT_FP64-34]; + Q_sqrt2={{(C_MANT_FP64-12){1'b0}},Qcnt_three_5[16:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000110: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-35:C_MANT_FP64-36]; + Q_sqrt0={{(C_MANT_FP64-13){1'b0}},Qcnt_three_6[19:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-37:C_MANT_FP64-38]; + Q_sqrt1={{(C_MANT_FP64-14){1'b0}},Qcnt_three_6[19:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-39:C_MANT_FP64-40]; + Q_sqrt2={{(C_MANT_FP64-15){1'b0}},Qcnt_three_6[19:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b000111: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-41:C_MANT_FP64-42]; + Q_sqrt0={{(C_MANT_FP64-16){1'b0}},Qcnt_three_7[22:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-43:C_MANT_FP64-44]; + Q_sqrt1={{(C_MANT_FP64-17){1'b0}},Qcnt_three_7[22:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-45:C_MANT_FP64-46]; + Q_sqrt2={{(C_MANT_FP64-18){1'b0}},Qcnt_three_7[22:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-47:C_MANT_FP64-48]; + Q_sqrt0={{(C_MANT_FP64-19){1'b0}},Qcnt_three_8[25:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-49:C_MANT_FP64-50]; + Q_sqrt1={{(C_MANT_FP64-20){1'b0}},Qcnt_three_8[25:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-51:C_MANT_FP64-52]; + Q_sqrt2={{(C_MANT_FP64-21){1'b0}},Qcnt_three_8[25:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-22){1'b0}},Qcnt_three_9[28:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-23){1'b0}},Qcnt_three_9[28:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-24){1'b0}},Qcnt_three_9[28:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-25){1'b0}},Qcnt_three_10[31:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-26){1'b0}},Qcnt_three_10[31:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-27){1'b0}},Qcnt_three_10[31:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-28){1'b0}},Qcnt_three_11[34:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-29){1'b0}},Qcnt_three_11[34:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-30){1'b0}},Qcnt_three_11[34:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-31){1'b0}},Qcnt_three_12[37:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-32){1'b0}},Qcnt_three_12[37:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-33){1'b0}},Qcnt_three_12[37:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-34){1'b0}},Qcnt_three_13[40:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-35){1'b0}},Qcnt_three_13[40:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-36){1'b0}},Qcnt_three_13[40:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001110: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-37){1'b0}},Qcnt_three_14[43:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-38){1'b0}},Qcnt_three_14[43:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-39){1'b0}},Qcnt_three_14[43:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b001111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-40){1'b0}},Qcnt_three_15[46:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-41){1'b0}},Qcnt_three_15[46:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-42){1'b0}},Qcnt_three_15[46:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b010000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-43){1'b0}},Qcnt_three_16[49:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-44){1'b0}},Qcnt_three_16[49:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-45){1'b0}},Qcnt_three_16[49:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b010001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-46){1'b0}},Qcnt_three_17[52:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-47){1'b0}},Qcnt_three_17[52:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-48){1'b0}},Qcnt_three_17[52:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + 6'b010010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-49){1'b0}},Qcnt_three_18[55:2]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-50){1'b0}},Qcnt_three_18[55:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-51){1'b0}},Qcnt_three_18[55:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + + default : + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_three_0[2]}; + Sqrt_Q0=Q_sqrt_com_0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt1={{(C_MANT_FP64+4){1'b0}},Qcnt_three_0[2:1]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-3:C_MANT_FP64-4]; + Q_sqrt2={{(C_MANT_FP64+3){1'b0}},Qcnt_three_0[2:0]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + end + endcase + + end + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b10, end // + ///////////////////////////////////////////////////////////////////////////// + + + 2'b11: + begin + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b11, start // + ///////////////////////////////////////////////////////////////////////////// + + case(Crtl_cnt_S) + + 6'b000000: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_four_0[3]}; + Sqrt_Q0=Q_sqrt_com_0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt1={{(C_MANT_FP64+4){1'b0}},Qcnt_four_0[3:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-3:C_MANT_FP64-4]; + Q_sqrt2={{(C_MANT_FP64+3){1'b0}},Qcnt_four_0[3:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-5:C_MANT_FP64-6]; + Q_sqrt3={{(C_MANT_FP64+2){1'b0}},Qcnt_four_0[3:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000001: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-7:C_MANT_FP64-8]; + Q_sqrt0={{(C_MANT_FP64+1){1'b0}},Qcnt_four_1[6:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-9:C_MANT_FP64-10]; + Q_sqrt1={{(C_MANT_FP64){1'b0}},Qcnt_four_1[6:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-11:C_MANT_FP64-12]; + Q_sqrt2={{(C_MANT_FP64-1){1'b0}},Qcnt_four_1[6:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-13:C_MANT_FP64-14]; + Q_sqrt3={{(C_MANT_FP64-2){1'b0}},Qcnt_four_1[6:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000010: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-15:C_MANT_FP64-16]; + Q_sqrt0={{(C_MANT_FP64-3){1'b0}},Qcnt_four_2[10:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-17:C_MANT_FP64-18]; + Q_sqrt1={{(C_MANT_FP64-4){1'b0}},Qcnt_four_2[10:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-19:C_MANT_FP64-20]; + Q_sqrt2={{(C_MANT_FP64-5){1'b0}},Qcnt_four_2[10:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-21:C_MANT_FP64-22]; + Q_sqrt3={{(C_MANT_FP64-6){1'b0}},Qcnt_four_2[10:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000011: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-23:C_MANT_FP64-24]; + Q_sqrt0={{(C_MANT_FP64-7){1'b0}},Qcnt_four_3[14:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-25:C_MANT_FP64-26]; + Q_sqrt1={{(C_MANT_FP64-8){1'b0}},Qcnt_four_3[14:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-27:C_MANT_FP64-28]; + Q_sqrt2={{(C_MANT_FP64-9){1'b0}},Qcnt_four_3[14:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-29:C_MANT_FP64-30]; + Q_sqrt3={{(C_MANT_FP64-10){1'b0}},Qcnt_four_3[14:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000100: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-31:C_MANT_FP64-32]; + Q_sqrt0={{(C_MANT_FP64-11){1'b0}},Qcnt_four_4[18:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-33:C_MANT_FP64-34]; + Q_sqrt1={{(C_MANT_FP64-12){1'b0}},Qcnt_four_4[18:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-35:C_MANT_FP64-36]; + Q_sqrt2={{(C_MANT_FP64-13){1'b0}},Qcnt_four_4[18:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-37:C_MANT_FP64-38]; + Q_sqrt3={{(C_MANT_FP64-14){1'b0}},Qcnt_four_4[18:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000101: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-39:C_MANT_FP64-40]; + Q_sqrt0={{(C_MANT_FP64-15){1'b0}},Qcnt_four_5[22:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-41:C_MANT_FP64-42]; + Q_sqrt1={{(C_MANT_FP64-16){1'b0}},Qcnt_four_5[22:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-43:C_MANT_FP64-44]; + Q_sqrt2={{(C_MANT_FP64-17){1'b0}},Qcnt_four_5[22:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-45:C_MANT_FP64-46]; + Q_sqrt3={{(C_MANT_FP64-18){1'b0}},Qcnt_four_5[22:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000110: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64-47:C_MANT_FP64-48]; + Q_sqrt0={{(C_MANT_FP64-19){1'b0}},Qcnt_four_6[26:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-49:C_MANT_FP64-50]; + Q_sqrt1={{(C_MANT_FP64-20){1'b0}},Qcnt_four_6[26:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-51:C_MANT_FP64-52]; + Q_sqrt2={{(C_MANT_FP64-21){1'b0}},Qcnt_four_6[26:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-22){1'b0}},Qcnt_four_6[26:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b000111: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-23){1'b0}},Qcnt_four_7[30:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-24){1'b0}},Qcnt_four_7[30:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-25){1'b0}},Qcnt_four_7[30:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-26){1'b0}},Qcnt_four_7[30:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b001000: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-27){1'b0}},Qcnt_four_8[34:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-28){1'b0}},Qcnt_four_8[34:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-29){1'b0}},Qcnt_four_8[34:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-30){1'b0}},Qcnt_four_8[34:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b001001: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-31){1'b0}},Qcnt_four_9[38:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-32){1'b0}},Qcnt_four_9[38:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-33){1'b0}},Qcnt_four_9[38:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-34){1'b0}},Qcnt_four_9[38:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b001010: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-35){1'b0}},Qcnt_four_10[42:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-36){1'b0}},Qcnt_four_10[42:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-37){1'b0}},Qcnt_four_10[42:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-38){1'b0}},Qcnt_four_10[42:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b001011: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-39){1'b0}},Qcnt_four_11[46:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-40){1'b0}},Qcnt_four_11[46:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-41){1'b0}},Qcnt_four_11[46:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-42){1'b0}},Qcnt_four_11[46:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b001100: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-43){1'b0}},Qcnt_four_12[50:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-44){1'b0}},Qcnt_four_12[50:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-45){1'b0}},Qcnt_four_12[50:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-46){1'b0}},Qcnt_four_12[50:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + 6'b001101: + begin + Sqrt_DI[0]=2'b00; + Q_sqrt0={{(C_MANT_FP64-47){1'b0}},Qcnt_four_13[54:3]}; + Sqrt_Q0=Quotient_DP[0]?Q_sqrt_com_0:Q_sqrt0; + Sqrt_DI[1]=2'b00; + Q_sqrt1={{(C_MANT_FP64-48){1'b0}},Qcnt_four_13[54:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=2'b00; + Q_sqrt2={{(C_MANT_FP64-49){1'b0}},Qcnt_four_13[54:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=2'b00; + Q_sqrt3={{(C_MANT_FP64-50){1'b0}},Qcnt_four_13[54:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + + default: + begin + Sqrt_DI[0]=Mant_D_sqrt_Norm[C_MANT_FP64+1:C_MANT_FP64]; + Q_sqrt0={{(C_MANT_FP64+5){1'b0}},Qcnt_four_0[3]}; + Sqrt_Q0=Q_sqrt_com_0; + Sqrt_DI[1]=Mant_D_sqrt_Norm[C_MANT_FP64-1:C_MANT_FP64-2]; + Q_sqrt1={{(C_MANT_FP64+4){1'b0}},Qcnt_four_0[3:2]}; + Sqrt_Q1=Sqrt_quotinent_S[3]?Q_sqrt_com_1:Q_sqrt1; + Sqrt_DI[2]=Mant_D_sqrt_Norm[C_MANT_FP64-3:C_MANT_FP64-4]; + Q_sqrt2={{(C_MANT_FP64+3){1'b0}},Qcnt_four_0[3:1]}; + Sqrt_Q2=Sqrt_quotinent_S[2]?Q_sqrt_com_2:Q_sqrt2; + Sqrt_DI[3]=Mant_D_sqrt_Norm[C_MANT_FP64-5:C_MANT_FP64-6]; + Q_sqrt3={{(C_MANT_FP64+2){1'b0}},Qcnt_four_0[3:0]}; + Sqrt_Q3=Sqrt_quotinent_S[1]?Q_sqrt_com_3:Q_sqrt3; + end + endcase + end + endcase + ///////////////////////////////////////////////////////////////////////////// + // Operands for square root when Iteration_unit_num_S = 2'b11, end // + ///////////////////////////////////////////////////////////////////////////// + end + + + + assign Sqrt_R0= ((Sqrt_start_dly_S)?'0:{Partial_remainder_DP[C_MANT_FP64+5:0]}); + assign Sqrt_R1= {Iteration_cell_sum_AMASK_D[0][C_MANT_FP64+5],Iteration_cell_sum_AMASK_D[0][C_MANT_FP64+2:0],Sqrt_DO[0]} ; + assign Sqrt_R2= {Iteration_cell_sum_AMASK_D[1][C_MANT_FP64+5],Iteration_cell_sum_AMASK_D[1][C_MANT_FP64+2:0],Sqrt_DO[1]}; + assign Sqrt_R3= {Iteration_cell_sum_AMASK_D[2][C_MANT_FP64+5],Iteration_cell_sum_AMASK_D[2][C_MANT_FP64+2:0],Sqrt_DO[2]}; + assign Sqrt_R4= {Iteration_cell_sum_AMASK_D[3][C_MANT_FP64+5],Iteration_cell_sum_AMASK_D[3][C_MANT_FP64+2:0],Sqrt_DO[3]}; + + logic [C_MANT_FP64+5:0] Denominator_se_format_DB; // + + assign Denominator_se_format_DB={Denominator_se_DB[C_MANT_FP64+1:C_MANT_FP64-C_MANT_FP16ALT],{FP16ALT_SO?FP16ALT_SO:Denominator_se_DB[C_MANT_FP64-C_MANT_FP16ALT-1]}, + Denominator_se_DB[C_MANT_FP64-C_MANT_FP16ALT-2:C_MANT_FP64-C_MANT_FP16],{FP16_SO?FP16_SO:Denominator_se_DB[C_MANT_FP64-C_MANT_FP16-1]}, + Denominator_se_DB[C_MANT_FP64-C_MANT_FP16-2:C_MANT_FP64-C_MANT_FP32],{FP32_SO?FP32_SO:Denominator_se_DB[C_MANT_FP64-C_MANT_FP32-1]}, + Denominator_se_DB[C_MANT_FP64-C_MANT_FP32-2:C_MANT_FP64-C_MANT_FP64],FP64_SO,3'b0} ; + // for iteration cell_U0 + logic [C_MANT_FP64+5:0] First_iteration_cell_div_a_D,First_iteration_cell_div_b_D; + logic Sel_b_for_first_S; + + + assign First_iteration_cell_div_a_D=(Div_start_dly_S)?{Numerator_se_D[C_MANT_FP64+1:C_MANT_FP64-C_MANT_FP16ALT],{FP16ALT_SO?FP16ALT_SO:Numerator_se_D[C_MANT_FP64-C_MANT_FP16ALT-1]}, + Numerator_se_D[C_MANT_FP64-C_MANT_FP16ALT-2:C_MANT_FP64-C_MANT_FP16],{FP16_SO?FP16_SO:Numerator_se_D[C_MANT_FP64-C_MANT_FP16-1]}, + Numerator_se_D[C_MANT_FP64-C_MANT_FP16-2:C_MANT_FP64-C_MANT_FP32],{FP32_SO?FP32_SO:Numerator_se_D[C_MANT_FP64-C_MANT_FP32-1]}, + Numerator_se_D[C_MANT_FP64-C_MANT_FP32-2:C_MANT_FP64-C_MANT_FP64],FP64_SO,3'b0} + :{Partial_remainder_DP[C_MANT_FP64+4:C_MANT_FP64-C_MANT_FP16ALT+3],{FP16ALT_SO?Quotient_DP[0]:Partial_remainder_DP[C_MANT_FP64-C_MANT_FP16ALT+2]}, + Partial_remainder_DP[C_MANT_FP64-C_MANT_FP16ALT+1:C_MANT_FP64-C_MANT_FP16+3],{FP16_SO?Quotient_DP[0]:Partial_remainder_DP[C_MANT_FP64-C_MANT_FP16+2]}, + Partial_remainder_DP[C_MANT_FP64-C_MANT_FP16+1:C_MANT_FP64-C_MANT_FP32+3],{FP32_SO?Quotient_DP[0]:Partial_remainder_DP[C_MANT_FP64-C_MANT_FP32+2]}, + Partial_remainder_DP[C_MANT_FP64-C_MANT_FP32+1:C_MANT_FP64-C_MANT_FP64+3],FP64_SO&&Quotient_DP[0],3'b0}; + assign Sel_b_for_first_S=(Div_start_dly_S)?1:Quotient_DP[0]; + assign First_iteration_cell_div_b_D=Sel_b_for_first_S?Denominator_se_format_DB:{Denominator_se_D,4'b0}; + assign Iteration_cell_a_BMASK_D[0]=Sqrt_enable_SO?Sqrt_R0:{First_iteration_cell_div_a_D}; + assign Iteration_cell_b_BMASK_D[0]=Sqrt_enable_SO?Sqrt_Q0:{First_iteration_cell_div_b_D}; + + + + // for iteration cell_U1 + logic [C_MANT_FP64+5:0] Sec_iteration_cell_div_a_D,Sec_iteration_cell_div_b_D; + logic Sel_b_for_sec_S; + generate + if(|Iteration_unit_num_S) + begin + assign Sel_b_for_sec_S=~Iteration_cell_sum_AMASK_D[0][C_MANT_FP64+5]; + assign Sec_iteration_cell_div_a_D={Iteration_cell_sum_AMASK_D[0][C_MANT_FP64+4:C_MANT_FP64-C_MANT_FP16ALT+3],{FP16ALT_SO?Sel_b_for_sec_S:Iteration_cell_sum_AMASK_D[0][C_MANT_FP64-C_MANT_FP16ALT+2]}, + Iteration_cell_sum_AMASK_D[0][C_MANT_FP64-C_MANT_FP16ALT+1:C_MANT_FP64-C_MANT_FP16+3],{FP16_SO?Sel_b_for_sec_S:Iteration_cell_sum_AMASK_D[0][C_MANT_FP64-C_MANT_FP16+2]}, + Iteration_cell_sum_AMASK_D[0][C_MANT_FP64-C_MANT_FP16+1:C_MANT_FP64-C_MANT_FP32+3],{FP32_SO?Sel_b_for_sec_S:Iteration_cell_sum_AMASK_D[0][C_MANT_FP64-C_MANT_FP32+2]}, + Iteration_cell_sum_AMASK_D[0][C_MANT_FP64-C_MANT_FP32+1:C_MANT_FP64-C_MANT_FP64+3],FP64_SO&&Sel_b_for_sec_S,3'b0}; + assign Sec_iteration_cell_div_b_D=Sel_b_for_sec_S?Denominator_se_format_DB:{Denominator_se_D,4'b0}; + assign Iteration_cell_a_BMASK_D[1]=Sqrt_enable_SO?Sqrt_R1:{Sec_iteration_cell_div_a_D}; + assign Iteration_cell_b_BMASK_D[1]=Sqrt_enable_SO?Sqrt_Q1:{Sec_iteration_cell_div_b_D}; + end + endgenerate + + // for iteration cell_U2 + logic [C_MANT_FP64+5:0] Thi_iteration_cell_div_a_D,Thi_iteration_cell_div_b_D; + logic Sel_b_for_thi_S; + generate + if((Iteration_unit_num_S==2'b10) | (Iteration_unit_num_S==2'b11)) + begin + assign Sel_b_for_thi_S=~Iteration_cell_sum_AMASK_D[1][C_MANT_FP64+5]; + assign Thi_iteration_cell_div_a_D={Iteration_cell_sum_AMASK_D[1][C_MANT_FP64+4:C_MANT_FP64-C_MANT_FP16ALT+3],{FP16ALT_SO?Sel_b_for_thi_S:Iteration_cell_sum_AMASK_D[1][C_MANT_FP64-C_MANT_FP16ALT+2]}, + Iteration_cell_sum_AMASK_D[1][C_MANT_FP64-C_MANT_FP16ALT+1:C_MANT_FP64-C_MANT_FP16+3],{FP16_SO?Sel_b_for_thi_S:Iteration_cell_sum_AMASK_D[1][C_MANT_FP64-C_MANT_FP16+2]}, + Iteration_cell_sum_AMASK_D[1][C_MANT_FP64-C_MANT_FP16+1:C_MANT_FP64-C_MANT_FP32+3],{FP32_SO?Sel_b_for_thi_S:Iteration_cell_sum_AMASK_D[1][C_MANT_FP64-C_MANT_FP32+2]}, + Iteration_cell_sum_AMASK_D[1][C_MANT_FP64-C_MANT_FP32+1:C_MANT_FP64-C_MANT_FP64+3],FP64_SO&&Sel_b_for_thi_S,3'b0}; + assign Thi_iteration_cell_div_b_D=Sel_b_for_thi_S?Denominator_se_format_DB:{Denominator_se_D,4'b0}; + assign Iteration_cell_a_BMASK_D[2]=Sqrt_enable_SO?Sqrt_R2:{Thi_iteration_cell_div_a_D}; + assign Iteration_cell_b_BMASK_D[2]=Sqrt_enable_SO?Sqrt_Q2:{Thi_iteration_cell_div_b_D}; + end + endgenerate + + // for iteration cell_U3 + logic [C_MANT_FP64+5:0] Fou_iteration_cell_div_a_D,Fou_iteration_cell_div_b_D; + logic Sel_b_for_fou_S; + + generate + if(Iteration_unit_num_S==2'b11) + begin + assign Sel_b_for_fou_S=~Iteration_cell_sum_AMASK_D[2][C_MANT_FP64+5]; + assign Fou_iteration_cell_div_a_D={Iteration_cell_sum_AMASK_D[2][C_MANT_FP64+4:C_MANT_FP64-C_MANT_FP16ALT+3],{FP16ALT_SO?Sel_b_for_fou_S:Iteration_cell_sum_AMASK_D[2][C_MANT_FP64-C_MANT_FP16ALT+2]}, + Iteration_cell_sum_AMASK_D[2][C_MANT_FP64-C_MANT_FP16ALT+1:C_MANT_FP64-C_MANT_FP16+3],{FP16_SO?Sel_b_for_fou_S:Iteration_cell_sum_AMASK_D[2][C_MANT_FP64-C_MANT_FP16+2]}, + Iteration_cell_sum_AMASK_D[2][C_MANT_FP64-C_MANT_FP16+1:C_MANT_FP64-C_MANT_FP32+3],{FP32_SO?Sel_b_for_fou_S:Iteration_cell_sum_AMASK_D[2][C_MANT_FP64-C_MANT_FP32+2]}, + Iteration_cell_sum_AMASK_D[2][C_MANT_FP64-C_MANT_FP32+1:C_MANT_FP64-C_MANT_FP64+3],FP64_SO&&Sel_b_for_fou_S,3'b0}; + assign Fou_iteration_cell_div_b_D=Sel_b_for_fou_S?Denominator_se_format_DB:{Denominator_se_D,4'b0}; + assign Iteration_cell_a_BMASK_D[3]=Sqrt_enable_SO?Sqrt_R3:{Fou_iteration_cell_div_a_D}; + assign Iteration_cell_b_BMASK_D[3]=Sqrt_enable_SO?Sqrt_Q3:{Fou_iteration_cell_div_b_D}; + end + endgenerate + + ///////////////////////////////////////////////////////////////////////////// + // Masking Contrl // + ///////////////////////////////////////////////////////////////////////////// + + + logic [C_MANT_FP64+1+4:0] Mask_bits_ctl_S; //For extension + + assign Mask_bits_ctl_S =58'h3ff_ffff_ffff_ffff; //It is not needed. The corresponding process is handled the above codes + + ///////////////////////////////////////////////////////////////////////////// + // Iteration Instances with masking control // + ///////////////////////////////////////////////////////////////////////////// + + + logic Div_enable_SI [3:0]; + logic Div_start_dly_SI [3:0]; + logic Sqrt_enable_SI [3:0]; + generate + genvar i,j; + for (i=0; i <= Iteration_unit_num_S ; i++) + begin + for (j = 0; j <= C_MANT_FP64+5; j++) begin + assign Iteration_cell_a_D[i][j] = Mask_bits_ctl_S[j] && Iteration_cell_a_BMASK_D[i][j]; + assign Iteration_cell_b_D[i][j] = Mask_bits_ctl_S[j] && Iteration_cell_b_BMASK_D[i][j]; + assign Iteration_cell_sum_AMASK_D[i][j] = Mask_bits_ctl_S[j] && Iteration_cell_sum_D[i][j]; + end + + assign Div_enable_SI[i] = Div_enable_SO; + assign Div_start_dly_SI[i] = Div_start_dly_S; + assign Sqrt_enable_SI[i] = Sqrt_enable_SO; + iteration_div_sqrt_mvp #(C_MANT_FP64+6) iteration_div_sqrt + ( + .A_DI (Iteration_cell_a_D[i] ), + .B_DI (Iteration_cell_b_D[i] ), + .Div_enable_SI (Div_enable_SI[i] ), + .Div_start_dly_SI (Div_start_dly_SI[i] ), + .Sqrt_enable_SI (Sqrt_enable_SI[i] ), + .D_DI (Sqrt_DI[i] ), + .D_DO (Sqrt_DO[i] ), + .Sum_DO (Iteration_cell_sum_D[i] ), + .Carry_out_DO (Iteration_cell_carry_D[i] ) + ); + + end + + endgenerate + + + + always_comb + begin + case (Iteration_unit_num_S) + 2'b00: + begin + if(Fsm_enable_S) + Partial_remainder_DN = Sqrt_enable_SO?Sqrt_R1:Iteration_cell_sum_AMASK_D[0]; + else + Partial_remainder_DN = Partial_remainder_DP; + end + 2'b01: + begin + if(Fsm_enable_S) + Partial_remainder_DN = Sqrt_enable_SO?Sqrt_R2:Iteration_cell_sum_AMASK_D[1]; + else + Partial_remainder_DN = Partial_remainder_DP; + end + 2'b10: + begin + if(Fsm_enable_S) + Partial_remainder_DN = Sqrt_enable_SO?Sqrt_R3:Iteration_cell_sum_AMASK_D[2]; + else + Partial_remainder_DN = Partial_remainder_DP; + end + 2'b11: + begin + if(Fsm_enable_S) + Partial_remainder_DN = Sqrt_enable_SO?Sqrt_R4:Iteration_cell_sum_AMASK_D[3]; + else + Partial_remainder_DN = Partial_remainder_DP; + end + endcase + end + + + + always_ff @(posedge Clk_CI, negedge Rst_RBI) // partial_remainder + begin + if(~Rst_RBI) + begin + Partial_remainder_DP <= '0; + end + else + begin + Partial_remainder_DP <= Partial_remainder_DN; + end + end + + logic [C_MANT_FP64+4:0] Quotient_DN; + + always_comb // Can choosen the different carry-outs based on different operations + begin + case (Iteration_unit_num_S) + 2'b00: + begin + if(Fsm_enable_S) + Quotient_DN= Sqrt_enable_SO ? {Quotient_DP[C_MANT_FP64+3:0],Sqrt_quotinent_S[3]} :{Quotient_DP[C_MANT_FP64+3:0],Iteration_cell_carry_D[0]}; + else + Quotient_DN= Quotient_DP; + end + 2'b01: + begin + if(Fsm_enable_S) + Quotient_DN= Sqrt_enable_SO ? {Quotient_DP[C_MANT_FP64+2:0],Sqrt_quotinent_S[3:2]} :{Quotient_DP[C_MANT_FP64+2:0],Iteration_cell_carry_D[0],Iteration_cell_carry_D[1]}; + else + Quotient_DN= Quotient_DP; + end + 2'b10: + begin + if(Fsm_enable_S) + Quotient_DN= Sqrt_enable_SO ? {Quotient_DP[C_MANT_FP64+1:0],Sqrt_quotinent_S[3:1]} : {Quotient_DP[C_MANT_FP64+1:0],Iteration_cell_carry_D[0],Iteration_cell_carry_D[1],Iteration_cell_carry_D[2]}; + else + Quotient_DN= Quotient_DP; + end + 2'b11: + begin + if(Fsm_enable_S) + Quotient_DN= Sqrt_enable_SO ? {Quotient_DP[C_MANT_FP64:0],Sqrt_quotinent_S } : {Quotient_DP[C_MANT_FP64:0],Iteration_cell_carry_D[0],Iteration_cell_carry_D[1],Iteration_cell_carry_D[2],Iteration_cell_carry_D[3]}; + else + Quotient_DN= Quotient_DP; + end + endcase + end + + always_ff @(posedge Clk_CI, negedge Rst_RBI) // Quotient + begin + if(~Rst_RBI) + begin + Quotient_DP <= '0; + end + else + Quotient_DP <= Quotient_DN; + end + + + ///////////////////////////////////////////////////////////////////////////// + // Precision Control for outputs // + ///////////////////////////////////////////////////////////////////////////// + + +//////////////////////one iteration unit, start/////////////////////////////////////// + generate + if(Iteration_unit_num_S==2'b00) + begin + always_comb + begin + case (Format_sel_S) + 2'b00: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+4:0],{(C_MANT_FP64-C_MANT_FP32){1'b0}}}; //+4 + end + 6'h17: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32:0],{(C_MANT_FP64-C_MANT_FP32+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h16: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-1:0],{(C_MANT_FP64-C_MANT_FP32+4+1){1'b0}}}; //Precision_ctl_S+1 + end + 6'h15: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-2:0],{(C_MANT_FP64-C_MANT_FP32+4+2){1'b0}}}; //Precision_ctl_S+1 + end + 6'h14: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-3:0],{(C_MANT_FP64-C_MANT_FP32+4+3){1'b0}}}; //Precision_ctl_S+1 + end + 6'h13: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-4:0],{(C_MANT_FP64-C_MANT_FP32+4+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h12: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-5:0],{(C_MANT_FP64-C_MANT_FP32+4+5){1'b0}}}; //Precision_ctl_S+1 + end + 6'h11: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-6:0],{(C_MANT_FP64-C_MANT_FP32+4+6){1'b0}}}; //Precision_ctl_S+1 + end + 6'h10: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-7:0],{(C_MANT_FP64-C_MANT_FP32+4+7){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0f: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-8:0],{(C_MANT_FP64-C_MANT_FP32+4+8){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-9:0],{(C_MANT_FP64-C_MANT_FP32+4+9){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0d: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-10:0],{(C_MANT_FP64-C_MANT_FP32+4+10){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-11:0],{(C_MANT_FP64-C_MANT_FP32+4+11){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0b: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-12:0],{(C_MANT_FP64-C_MANT_FP32+4+12){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-13:0],{(C_MANT_FP64-C_MANT_FP32+4+13){1'b0}}}; //Precision_ctl_S+1 + end + 6'h09: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-14:0],{(C_MANT_FP64-C_MANT_FP32+4+14){1'b0}}}; //Precision_ctl_S+1 + end + 6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-15:0],{(C_MANT_FP64-C_MANT_FP32+4+15){1'b0}}}; //Precision_ctl_S+1 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-16:0],{(C_MANT_FP64-C_MANT_FP32+4+16){1'b0}}}; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+4:0],{(C_MANT_FP64-C_MANT_FP32){1'b0}}}; //+4 + end + endcase + end + + 2'b01: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = Quotient_DP[C_MANT_FP64+4:0]; //+4 + end + 6'h34: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64:0],{(4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h33: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-1:0],{(4+1){1'b0}}}; //Precision_ctl_S+1 + end + 6'h32: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-2:0],{(4+2){1'b0}}}; //Precision_ctl_S+1 + end + 6'h31: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-3:0],{(4+3){1'b0}}}; //Precision_ctl_S+1 + end + 6'h30: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-4:0],{(4+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h2f: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-5:0],{(4+5){1'b0}}}; //Precision_ctl_S+1 + end + 6'h2e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-6:0],{(4+6){1'b0}}}; //Precision_ctl_S+1 + end + 6'h2d: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-7:0],{(4+7){1'b0}}}; //Precision_ctl_S+1 + end + 6'h2c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-8:0],{(4+8){1'b0}}}; //Precision_ctl_S+1 + end + 6'h2b: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-9:0],{(4+9){1'b0}}}; //Precision_ctl_S+1 + end + 6'h2a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-10:0],{(4+10){1'b0}}}; //Precision_ctl_S+1 + end + 6'h29: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-11:0],{(4+11){1'b0}}}; //Precision_ctl_S+1 + end + 6'h28: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-12:0],{(4+12){1'b0}}}; //Precision_ctl_S+1 + end + 6'h27: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-13:0],{(4+13){1'b0}}}; //Precision_ctl_S+1 + end + 6'h26: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-14:0],{(4+14){1'b0}}}; //Precision_ctl_S+1 + end + 6'h25: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-15:0],{(4+15){1'b0}}}; //Precision_ctl_S+1 + end + 6'h24: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-16:0],{(4+16){1'b0}}}; //Precision_ctl_S+1 + end + 6'h23: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-17:0],{(4+17){1'b0}}}; //Precision_ctl_S+1 + end + 6'h22: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-18:0],{(4+18){1'b0}}}; //Precision_ctl_S+1 + end + 6'h21: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-19:0],{(4+19){1'b0}}}; //Precision_ctl_S+1 + end + 6'h20: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-20:0],{(4+20){1'b0}}}; //Precision_ctl_S+1 + end + 6'h1f: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-21:0],{(4+21){1'b0}}}; //Precision_ctl_S+1 + end + 6'h1e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-22:0],{(4+22){1'b0}}}; //Precision_ctl_S+1 + end + 6'h1d: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-23:0],{(4+23){1'b0}}}; //Precision_ctl_S+1 + end + 6'h1c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-24:0],{(4+24){1'b0}}}; //Precision_ctl_S+1 + end + 6'h1b: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-25:0],{(4+25){1'b0}}}; //Precision_ctl_S+1 + end + 6'h1a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-26:0],{(4+26){1'b0}}}; //Precision_ctl_S+1 + end + 6'h19: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-27:0],{(4+27){1'b0}}}; //Precision_ctl_S+1 + end + 6'h18: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-28:0],{(4+28){1'b0}}}; //Precision_ctl_S+1 + end + 6'h17: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-29:0],{(4+29){1'b0}}}; //Precision_ctl_S+1 + end + 6'h16: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-30:0],{(4+30){1'b0}}}; //Precision_ctl_S+1 + end + 6'h15: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-31:0],{(4+31){1'b0}}}; //Precision_ctl_S+1 + end + 6'h14: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-32:0],{(4+32){1'b0}}}; //Precision_ctl_S+1 + end + 6'h13: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-33:0],{(4+33){1'b0}}}; //Precision_ctl_S+1 + end + 6'h12: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-34:0],{(4+34){1'b0}}}; //Precision_ctl_S+1 + end + 6'h11: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-35:0],{(4+35){1'b0}}}; //Precision_ctl_S+1 + end + 6'h10: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-36:0],{(4+36){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0f: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-37:0],{(4+37){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-38:0],{(4+38){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0d: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-39:0],{(4+39){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-40:0],{(4+40){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0b: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-41:0],{(4+41){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-42:0],{(4+42){1'b0}}}; //Precision_ctl_S+1 + end + 6'h09: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-43:0],{(4+43){1'b0}}}; //Precision_ctl_S+1 + end + 6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-44:0],{(4+44){1'b0}}}; //Precision_ctl_S+1 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-45:0],{(4+45){1'b0}}}; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = Quotient_DP[C_MANT_FP64+4:0]; //+4 + end + endcase + end + + 2'b10: + begin + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+4:0],{(C_MANT_FP64-C_MANT_FP16){1'b0}}}; //+4 + end + 6'h0a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16:0],{(C_MANT_FP64-C_MANT_FP16+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h09: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16-1:0],{(C_MANT_FP64-C_MANT_FP16+4+1){1'b0}}}; //Precision_ctl_S+1 + end + 6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16-2:0],{(C_MANT_FP64-C_MANT_FP16+4+2){1'b0}}}; //Precision_ctl_S+1 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16-3:0],{(C_MANT_FP64-C_MANT_FP16+4+3){1'b0}}}; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+4:0],{(C_MANT_FP64-C_MANT_FP16){1'b0}}}; //+4 + end + endcase + end + + 2'b11: + begin + + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}}}; //+4 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT:0],{(C_MANT_FP64-C_MANT_FP16ALT+4){1'b0}}}; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}}}; //+4 + end + endcase + end + endcase + end + end + endgenerate +//////////////////////one iteration unit, end////////////////////////////////////////// + +//////////////////////two iteration units, start/////////////////////////////////////// + generate + if(Iteration_unit_num_S==2'b01) + begin + always_comb + begin + case (Format_sel_S) + 2'b00: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+4:0],{(C_MANT_FP64-C_MANT_FP32){1'b0}}}; //+4 + end + 6'h17,6'h16: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32:0],{(C_MANT_FP64-C_MANT_FP32+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h15,6'h14: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-2:0],{(C_MANT_FP64-C_MANT_FP32+4+2){1'b0}}}; //Precision_ctl_S+1 + end + 6'h13,6'h12: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-4:0],{(C_MANT_FP64-C_MANT_FP32+4+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h11,6'h10: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-6:0],{(C_MANT_FP64-C_MANT_FP32+4+6){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0f,6'h0e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-8:0],{(C_MANT_FP64-C_MANT_FP32+4+8){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0d,6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-10:0],{(C_MANT_FP64-C_MANT_FP32+4+10){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0b,6'h0a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-12:0],{(C_MANT_FP64-C_MANT_FP32+4+12){1'b0}}}; //Precision_ctl_S+1 + end + 6'h09,6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-14:0],{(C_MANT_FP64-C_MANT_FP32+4+14){1'b0}}}; //Precision_ctl_S+1 + end + 6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-16:0],{(C_MANT_FP64-C_MANT_FP32+4+16){1'b0}}}; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+4:0],{(C_MANT_FP64-C_MANT_FP32){1'b0}}}; //+4 + end + endcase + end + 2'b01: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+3:0],1'b0}; //+3 + end + 6'h34: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+1:1],{(4){1'b0}} }; //Precision_ctl_S+1 + end + 6'h33,6'h32: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-1:0],{(4+1){1'b0}} }; //Precision_ctl_S+1 + end + 6'h31,6'h30: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-3:0],{(4+3){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2f,6'h2e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-5:0],{(4+5){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2d,6'h2c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-7:0],{(4+7){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2b,6'h2a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-9:0],{(4+9){1'b0}} }; //Precision_ctl_S+1 + end + 6'h29,6'h28: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-11:0],{(4+11){1'b0}} }; //Precision_ctl_S+1 + end + 6'h27,6'h26: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-13:0],{(4+13){1'b0}} }; //Precision_ctl_S+1 + end + 6'h25,6'h24: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-15:0],{(4+15){1'b0}} }; //Precision_ctl_S+1 + end + 6'h23,6'h22: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-17:0],{(4+17){1'b0}} }; //Precision_ctl_S+1 + end + 6'h21,6'h20: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-19:0],{(4+19){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1f,6'h1e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-21:0],{(4+21){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1d,6'h1c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-23:0],{(4+23){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1b,6'h1a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-25:0],{(4+25){1'b0}} }; //Precision_ctl_S+1 + end + 6'h19,6'h18: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-27:0],{(4+27){1'b0}} }; //Precision_ctl_S+1 + end + 6'h17,6'h16: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-29:0],{(4+29){1'b0}} }; //Precision_ctl_S+1 + end + 6'h15,6'h14: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-31:0],{(4+31){1'b0}} }; //Precision_ctl_S+1 + end + 6'h13,6'h12: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-33:0],{(4+33){1'b0}} }; //Precision_ctl_S+1 + end + 6'h11,6'h10: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-35:0],{(4+35){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0f,6'h0e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-37:0],{(4+37){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0d,6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-39:0],{(4+39){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0b,6'h0a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-41:0],{(4+41){1'b0}} }; //Precision_ctl_S+1 + end + 6'h09,6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-43:0],{(4+43){1'b0}} }; //Precision_ctl_S+1 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-45:0],{(4+45){1'b0}} }; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+3:0],1'b0}; //+3 + end + endcase + end + + 2'b10: + begin + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+3:0],{(C_MANT_FP64-C_MANT_FP16+1){1'b0}} }; //+3 + end + 6'h0a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+1:1],{(C_MANT_FP64-C_MANT_FP16+4){1'b0}} }; //Precision_ctl_S+1 + end + 6'h09,6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16-1:0],{(C_MANT_FP64-C_MANT_FP16+4+1){1'b0}} }; //Precision_ctl_S+1 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16-3:0],{(C_MANT_FP64-C_MANT_FP16+4+3){1'b0}} }; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+4:0],{(C_MANT_FP64-C_MANT_FP16){1'b0}} }; //+4 + end + endcase + end + + 2'b11: + begin + + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; //+4 + end + 6'h07: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT:0],{(C_MANT_FP64-C_MANT_FP16ALT+4){1'b0}} }; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; //+4 + end + endcase + end + endcase + end + end + endgenerate +//////////////////////two iteration units, end////////////////////////////////////////// + +//////////////////////three iteration units, start/////////////////////////////////////// + generate + if(Iteration_unit_num_S==2'b10) + begin + always_comb + begin + case (Format_sel_S) + 2'b00: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+3:0],{(C_MANT_FP64-C_MANT_FP32+1){1'b0}}}; //+3 + end + 6'h17,6'h16,6'h15: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32:0],{(C_MANT_FP64-C_MANT_FP32+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h14,6'h13,6'h12: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-3:0],{(C_MANT_FP64-C_MANT_FP32+4+3){1'b0}}}; //Precision_ctl_S+1 + end + 6'h11,6'h10,6'h0f: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-6:0],{(C_MANT_FP64-C_MANT_FP32+4+6){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0e,6'h0d,6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-9:0],{(C_MANT_FP64-C_MANT_FP32+4+9){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0b,6'h0a,6'h09: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-12:0],{(C_MANT_FP64-C_MANT_FP32+4+12){1'b0}}}; //Precision_ctl_S+1 + end + 6'h08,6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-15:0],{(C_MANT_FP64-C_MANT_FP32+4+15){1'b0}}}; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+3:0],{(C_MANT_FP64-C_MANT_FP32+1){1'b0}}}; //+3 + end + endcase + end + + 2'b01: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = Quotient_DP[C_MANT_FP64+4:0]; //+4 + end + 6'h34,6'h33: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+1:1],{(4){1'b0}} }; //Precision_ctl_S+1 + end + 6'h32,6'h31,6'h30: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-2:0],{(4+2){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2f,6'h2e,6'h2d: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-5:0],{(4+5){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2c,6'h2b,6'h2a: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-8:0],{(4+8){1'b0}} }; //Precision_ctl_S+1 + end + 6'h29,6'h28,6'h27: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-11:0],{(4+11){1'b0}} }; //Precision_ctl_S+1 + end + 6'h26,6'h25,6'h24: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-14:0],{(4+14){1'b0}} }; //Precision_ctl_S+1 + end + 6'h23,6'h22,6'h21: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-17:0],{(4+17){1'b0}} }; //Precision_ctl_S+1 + end + 6'h20,6'h1f,6'h1e: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-20:0],{(4+20){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1d,6'h1c,6'h1b: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-23:0],{(4+23){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1a,6'h19,6'h18: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-26:0],{(4+26){1'b0}} }; //Precision_ctl_S+1 + end + 6'h17,6'h16,6'h15: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-29:0],{(4+29){1'b0}} }; //Precision_ctl_S+1 + end + 6'h14,6'h13,6'h12: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-32:0],{(4+32){1'b0}} }; //Precision_ctl_S+1 + end + 6'h11,6'h10,6'h0f: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-35:0],{(4+35){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0e,6'h0d,6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-38:0],{(4+38){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0b,6'h0a,6'h09: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-41:0],{(4+41){1'b0}} }; //Precision_ctl_S+1 + end + 6'h08,6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-44:0],{(4+44){1'b0}} }; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = Quotient_DP[C_MANT_FP64+4:0]; //+4 + end + endcase + end + + 2'b10: + begin + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+4:0],{(C_MANT_FP64-C_MANT_FP16){1'b0}} }; //+4 + end + 6'h0a,6'h09: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+1:1],{(C_MANT_FP64-C_MANT_FP16+4){1'b0}} }; //Precision_ctl_S+1 + end + 6'h08,6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16-2:0],{(C_MANT_FP64-C_MANT_FP16+4+2){1'b0}} }; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+4:0],{(C_MANT_FP64-C_MANT_FP16){1'b0}} }; //+4 + end + endcase + end + + 2'b11: + begin + + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; //+4 + end + 6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+1:1],{(C_MANT_FP64-C_MANT_FP16ALT+4){1'b0}} }; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; //+4 + end + endcase + end + endcase + end + end + endgenerate +//////////////////////three iteration units, end////////////////////////////////////////// + +//////////////////////four iteration units, start/////////////////////////////////////// + generate + if(Iteration_unit_num_S==2'b11) + begin + always_comb + begin + case (Format_sel_S) + 2'b00: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+4:0],{(C_MANT_FP64-C_MANT_FP32){1'b0}}}; //+4 + end + 6'h17,6'h16,6'h15,6'h14: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32:0],{(C_MANT_FP64-C_MANT_FP32+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h13,6'h12,6'h11,6'h10: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-4:0],{(C_MANT_FP64-C_MANT_FP32+4+4){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0f,6'h0e,6'h0d,6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-8:0],{(C_MANT_FP64-C_MANT_FP32+4+8){1'b0}}}; //Precision_ctl_S+1 + end + 6'h0b,6'h0a,6'h09,6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-12:0],{(C_MANT_FP64-C_MANT_FP32+4+12){1'b0}}}; //Precision_ctl_S+1 + end + 6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32-16:0],{(C_MANT_FP64-C_MANT_FP32+4+16){1'b0}}}; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP32+4:0],{(C_MANT_FP64-C_MANT_FP32){1'b0}}}; //+4 + end + endcase + end + + 2'b01: + begin + case (Precision_ctl_S) + 6'h00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+3:0],{(1){1'b0}}}; //+3 + end + 6'h34: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+3:0],{(1){1'b0}} }; //Precision_ctl_S+1 + end + 6'h33,6'h32,6'h31,6'h30: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-1:0],{(5){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2f,6'h2e,6'h2d,6'h2c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-5:0],{(9){1'b0}} }; //Precision_ctl_S+1 + end + 6'h2b,6'h2a,6'h29,6'h28: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-9:0],{(13){1'b0}} }; //Precision_ctl_S+1 + end + 6'h27,6'h26,6'h25,6'h24: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-13:0],{(17){1'b0}} }; //Precision_ctl_S+1 + end + 6'h23,6'h22,6'h21,6'h20: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-17:0],{(21){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1f,6'h1e,6'h1d,6'h1c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-21:0],{(25){1'b0}} }; //Precision_ctl_S+1 + end + 6'h1b,6'h1a,6'h19,6'h18: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-25:0],{(29){1'b0}} }; //Precision_ctl_S+1 + end + 6'h17,6'h16,6'h15,6'h14: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-29:0],{(33){1'b0}} }; //Precision_ctl_S+1 + end + 6'h13,6'h12,6'h11,6'h10: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-33:0],{(37){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0f,6'h0e,6'h0d,6'h0c: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-37:0],{(41){1'b0}} }; //Precision_ctl_S+1 + end + 6'h0b,6'h0a,6'h09,6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-41:0],{(45){1'b0}} }; //Precision_ctl_S+1 + end + 6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64-45:0],{(49){1'b0}} }; //Precision_ctl_S+1 + end + default: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP64+3:0],{(1){1'b0}}}; //+3 + end + endcase + end + + 2'b10: + begin + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+5:0],{(C_MANT_FP64-C_MANT_FP16-1){1'b0}} }; //+5 + end + 6'h0a,6'h09,6'h08: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+1:1],{(C_MANT_FP64-C_MANT_FP16+4){1'b0}} }; //Precision_ctl_S+1 + end + 6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+1-4:0],{(C_MANT_FP64-C_MANT_FP16+4+3){1'b0}} }; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16+5:0],{(C_MANT_FP64-C_MANT_FP16-1){1'b0}} }; //+5 + end + endcase + end + + 2'b11: + begin + + case (Precision_ctl_S) + 6'b00: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; //+4 + end + 6'h07,6'h06: + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT:0],{(C_MANT_FP64-C_MANT_FP16ALT+4){1'b0}} }; //Precision_ctl_S+1 + end + default : + begin + Mant_result_prenorm_DO = {Quotient_DP[C_MANT_FP16ALT+4:0],{(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; //+4 + end + endcase + end + endcase + end + end + endgenerate +//////////////////////four iteration units, end/////////////////////////////////////// + + + + + +// resultant exponent + logic [C_EXP_FP64+1:0] Exp_result_prenorm_DN,Exp_result_prenorm_DP; + + logic [C_EXP_FP64+1:0] Exp_add_a_D; + logic [C_EXP_FP64+1:0] Exp_add_b_D; + logic [C_EXP_FP64+1:0] Exp_add_c_D; + + integer C_BIAS_AONE, C_HALF_BIAS; + always_comb + begin // + case (Format_sel_S) + 2'b00: + begin + C_BIAS_AONE =C_BIAS_AONE_FP32; + C_HALF_BIAS =C_HALF_BIAS_FP32; + end + 2'b01: + begin + C_BIAS_AONE =C_BIAS_AONE_FP64; + C_HALF_BIAS =C_HALF_BIAS_FP64; + end + 2'b10: + begin + C_BIAS_AONE =C_BIAS_AONE_FP16; + C_HALF_BIAS =C_HALF_BIAS_FP16; + end + 2'b11: + begin + C_BIAS_AONE =C_BIAS_AONE_FP16ALT; + C_HALF_BIAS =C_HALF_BIAS_FP16ALT; + end + endcase + end + +//For division, exponent=(Exp_a_D-LZ1)-(Exp_b_D-LZ2)+BIAS +//For square root, exponent=(Exp_a_D-LZ1)/2+(Exp_a_D-LZ1)%2+C_HALF_BIAS +//For exponent, in preprorces module, (Exp_a_D-LZ1) and (Exp_b_D-LZ2) have been processed with the corresponding process for denormal numbers. + + assign Exp_add_a_D = {Sqrt_start_dly_S?{Exp_num_DI[C_EXP_FP64],Exp_num_DI[C_EXP_FP64],Exp_num_DI[C_EXP_FP64],Exp_num_DI[C_EXP_FP64:1]}:{Exp_num_DI[C_EXP_FP64],Exp_num_DI[C_EXP_FP64],Exp_num_DI}}; + assign Exp_add_b_D = {Sqrt_start_dly_S?{1'b0,{C_EXP_ZERO_FP64},Exp_num_DI[0]}:{~Exp_den_DI[C_EXP_FP64],~Exp_den_DI[C_EXP_FP64],~Exp_den_DI}}; + assign Exp_add_c_D = {Div_start_dly_S?{{C_BIAS_AONE}}:{{C_HALF_BIAS}}}; + assign Exp_result_prenorm_DN = (Start_dly_S)?{Exp_add_a_D + Exp_add_b_D + Exp_add_c_D}:Exp_result_prenorm_DP; + + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Exp_result_prenorm_DP <= '0; + end + else + begin + Exp_result_prenorm_DP<= Exp_result_prenorm_DN; + end + end + + assign Exp_result_prenorm_DO = Exp_result_prenorm_DP; + +endmodule
diff --git a/verilog/rtl/data_mem_top.sv b/verilog/rtl/data_mem_top.sv new file mode 100644 index 0000000..19fb93e --- /dev/null +++ b/verilog/rtl/data_mem_top.sv
@@ -0,0 +1,67 @@ +module data_mem_top +( + input clk_i, + input rst_ni, + +// tl-ul insterface + input tlul_pkg::tl_h2d_t tl_d_i, + output tlul_pkg::tl_d2h_t tl_d_o, + +// sram interface + output logic csb, + output logic [11:0] addr_o, + output logic [31:0] wdata_o, + output logic [3:0] wmask_o, + output logic we_o, + input logic [31:0] rdata_i +); + + logic tl_req; + logic [31:0] tl_wmask; + logic we_i; + logic rvalid_o; + + assign wmask_o[0] = (tl_wmask[7:0] != 8'b0) ? 1'b1: 1'b0; + assign wmask_o[1] = (tl_wmask[15:8] != 8'b0) ? 1'b1: 1'b0; + assign wmask_o[2] = (tl_wmask[23:16] != 8'b0) ? 1'b1: 2'b0; + assign wmask_o[3] = (tl_wmask[31:24] != 8'b0) ? 1'b1: 2'b0; + + assign we_o = ~we_i; + assign csb = ~tl_req; + +tlul_sram_adapter #( + .SramAw (12), + .SramDw (32), + .Outstanding (4), + .ByteAccess (1), + .ErrOnWrite (0), // 1: Writes not allowed, automatically error + .ErrOnRead (0) + +) data_mem ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_i (tl_d_i), + .tl_o (tl_d_o), + .req_o (tl_req), + .gnt_i (1'b1), + .we_o (we_i), + .addr_o (addr_o), + .wdata_o (wdata_o), + .wmask_o (tl_wmask), + .rdata_i (rst_ni? rdata_i: '0), // (reset) ? rdata_o: '0 + .rvalid_i (rvalid_o), + .rerror_i (2'b0) + +); + + always_ff @(posedge clk_i) begin + if (!rst_ni) begin + rvalid_o <= 1'b0; + end else if (we_i) begin + rvalid_o <= 1'b0; + end else begin + rvalid_o <= tl_req; + end + end + +endmodule
diff --git a/verilog/rtl/debug_rom.sv b/verilog/rtl/debug_rom.sv new file mode 100644 index 0000000..528c62c --- /dev/null +++ b/verilog/rtl/debug_rom.sv
@@ -0,0 +1,65 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * File: $filename.v + * + * Description: Auto-generated bootrom + */ + +// Auto-generated code +module debug_rom ( + input logic clk_i, + input logic req_i, + input logic [63:0] addr_i, + output logic [63:0] rdata_o +); + + localparam int unsigned RomSize = 19; + logic [RomSize-1:0][63:0] mem; + assign mem = { + 64'h00000000_7b200073, + 64'h7b202473_7b302573, + 64'h10852423_f1402473, + 64'ha85ff06f_7b202473, + 64'h7b302573_10052223, + 64'h00100073_7b202473, + 64'h7b302573_10052623, + 64'h00c51513_00c55513, + 64'h00000517_fd5ff06f, + 64'hfa041ce3_00247413, + 64'h40044403_00a40433, + 64'hf1402473_02041c63, + 64'h00147413_40044403, + 64'h00a40433_10852023, + 64'hf1402473_00c51513, + 64'h00c55513_00000517, + 64'h7b351073_7b241073, + 64'h0ff0000f_04c0006f, + 64'h07c0006f_00c0006f + }; + + logic [$clog2(RomSize)-1:0] addr_q; + + always_ff @(posedge clk_i) begin + if (req_i) begin + addr_q <= addr_i[$clog2(RomSize)-1+3:3]; + end + end + + // this prevents spurious Xes from propagating into + // the speculative fetch stage of the core + always_comb begin : p_outmux + rdata_o = '0; + if (addr_q < $clog2(RomSize)'(RomSize)) begin + rdata_o = mem[addr_q]; + end + end + +endmodule
diff --git a/verilog/rtl/debug_rom_one_scratch.sv b/verilog/rtl/debug_rom_one_scratch.sv new file mode 100644 index 0000000..3e88406 --- /dev/null +++ b/verilog/rtl/debug_rom_one_scratch.sv
@@ -0,0 +1,60 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * File: $filename.v + * + * Description: Auto-generated bootrom + */ + +// Auto-generated code +module debug_rom_one_scratch ( + input logic clk_i, + input logic req_i, + input logic [63:0] addr_i, + output logic [63:0] rdata_o +); + + localparam int unsigned RomSize = 13; + logic [RomSize-1:0][63:0] mem; + + assign mem = { + 64'h00000000_7b200073, + 64'h7b202473_10802423, + 64'hf1402473_ab1ff06f, + 64'h7b202473_10002223, + 64'h00100073_7b202473, + 64'h10002623_fddff06f, + 64'hfc0418e3_00247413, + 64'h40044403_f1402473, + 64'h02041263_00147413, + 64'h40044403_10802023, + 64'hf1402473_7b241073, + 64'h0ff0000f_0340006f, + 64'h0500006f_00c0006f + }; + + logic [$clog2(RomSize)-1:0] addr_q; + + always_ff @(posedge clk_i) begin + if (req_i) begin + addr_q <= addr_i[$clog2(RomSize)-1+3:3]; + end + end + + // this prevents spurious Xes from propagating into + // the speculative fetch stage of the core + always_comb begin : p_outmux + rdata_o = '0; + if (addr_q < $clog2(RomSize)'(RomSize)) begin + rdata_o = mem[addr_q]; + end + end + +endmodule
diff --git a/verilog/rtl/defs_div_sqrt_mvp.sv b/verilog/rtl/defs_div_sqrt_mvp.sv new file mode 100644 index 0000000..b3f41fe --- /dev/null +++ b/verilog/rtl/defs_div_sqrt_mvp.sv
@@ -0,0 +1,83 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// This file contains all div_sqrt_top_mvp parameters +// Authors : Lei Li (lile@iis.ee.ethz.ch) + +package defs_div_sqrt_mvp; + + // op command + localparam C_RM = 3; + localparam C_RM_NEAREST = 3'h0; + localparam C_RM_TRUNC = 3'h1; + localparam C_RM_PLUSINF = 3'h2; + localparam C_RM_MINUSINF = 3'h3; + localparam C_PC = 6; // Precision Control + localparam C_FS = 2; // Format Selection + localparam C_IUNC = 2; // Iteration Unit Number Control + localparam Iteration_unit_num_S = 2'b10; + + // FP64 + localparam C_OP_FP64 = 64; + localparam C_MANT_FP64 = 52; + localparam C_EXP_FP64 = 11; + localparam C_BIAS_FP64 = 1023; + localparam C_BIAS_AONE_FP64 = 11'h400; + localparam C_HALF_BIAS_FP64 = 511; + localparam C_EXP_ZERO_FP64 = 11'h000; + localparam C_EXP_ONE_FP64 = 13'h001; // Bit width is in agreement with in norm + localparam C_EXP_INF_FP64 = 11'h7FF; + localparam C_MANT_ZERO_FP64 = 52'h0; + localparam C_MANT_NAN_FP64 = 52'h8_0000_0000_0000; + localparam C_PZERO_FP64 = 64'h0000_0000_0000_0000; + localparam C_MZERO_FP64 = 64'h8000_0000_0000_0000; + localparam C_QNAN_FP64 = 64'h7FF8_0000_0000_0000; + + // FP32 + localparam C_OP_FP32 = 32; + localparam C_MANT_FP32 = 23; + localparam C_EXP_FP32 = 8; + localparam C_BIAS_FP32 = 127; + localparam C_BIAS_AONE_FP32 = 8'h80; + localparam C_HALF_BIAS_FP32 = 63; + localparam C_EXP_ZERO_FP32 = 8'h00; + localparam C_EXP_INF_FP32 = 8'hFF; + localparam C_MANT_ZERO_FP32 = 23'h0; + localparam C_PZERO_FP32 = 32'h0000_0000; + localparam C_MZERO_FP32 = 32'h8000_0000; + localparam C_QNAN_FP32 = 32'h7FC0_0000; + + // FP16 + localparam C_OP_FP16 = 16; + localparam C_MANT_FP16 = 10; + localparam C_EXP_FP16 = 5; + localparam C_BIAS_FP16 = 15; + localparam C_BIAS_AONE_FP16 = 5'h10; + localparam C_HALF_BIAS_FP16 = 7; + localparam C_EXP_ZERO_FP16 = 5'h00; + localparam C_EXP_INF_FP16 = 5'h1F; + localparam C_MANT_ZERO_FP16 = 10'h0; + localparam C_PZERO_FP16 = 16'h0000; + localparam C_MZERO_FP16 = 16'h8000; + localparam C_QNAN_FP16 = 16'h7E00; + + // FP16alt + localparam C_OP_FP16ALT = 16; + localparam C_MANT_FP16ALT = 7; + localparam C_EXP_FP16ALT = 8; + localparam C_BIAS_FP16ALT = 127; + localparam C_BIAS_AONE_FP16ALT = 8'h80; + localparam C_HALF_BIAS_FP16ALT = 63; + localparam C_EXP_ZERO_FP16ALT = 8'h00; + localparam C_EXP_INF_FP16ALT = 8'hFF; + localparam C_MANT_ZERO_FP16ALT = 7'h0; + localparam C_QNAN_FP16ALT = 16'h7FC0; + +endpackage : defs_div_sqrt_mvp
diff --git a/verilog/rtl/div_sqrt_top_mvp.sv b/verilog/rtl/div_sqrt_top_mvp.sv new file mode 100644 index 0000000..3af6081 --- /dev/null +++ b/verilog/rtl/div_sqrt_top_mvp.sv
@@ -0,0 +1,180 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// Company: IIS @ ETHZ - Federal Institute of Technology // +// // +// Engineers: Lei Li -- lile@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 03/03/2018 // +// Design Name: div_sqrt_top_mvp // +// Module Name: div_sqrt_top_mvp.sv // +// Project Name: The shared divisor and square root // +// Language: SystemVerilog // +// // +// Description: The top of div and sqrt // +// // +// // +// Revision Date: 12/04/2018 // +// Lei Li // +// To address some requirements by Stefan and add low power // +// control for special cases // +//////////////////////////////////////////////////////////////////////////////// + +import defs_div_sqrt_mvp::*; + +module div_sqrt_top_mvp + + (//Input + input logic Clk_CI, + input logic Rst_RBI, + input logic Div_start_SI, + input logic Sqrt_start_SI, + + //Input Operands + input logic [C_OP_FP64-1:0] Operand_a_DI, + input logic [C_OP_FP64-1:0] Operand_b_DI, + + // Input Control + input logic [C_RM-1:0] RM_SI, //Rounding Mode + input logic [C_PC-1:0] Precision_ctl_SI, // Precision Control + input logic [C_FS-1:0] Format_sel_SI, // Format Selection, + input logic Kill_SI, + + //Output Result + output logic [C_OP_FP64-1:0] Result_DO, + + //Output-Flags + output logic [4:0] Fflags_SO, + output logic Ready_SO, + output logic Done_SO + ); + + + + + + //Operand components + logic [C_EXP_FP64:0] Exp_a_D; + logic [C_EXP_FP64:0] Exp_b_D; + logic [C_MANT_FP64:0] Mant_a_D; + logic [C_MANT_FP64:0] Mant_b_D; + + logic [C_EXP_FP64+1:0] Exp_z_D; + logic [C_MANT_FP64+4:0] Mant_z_D; + logic Sign_z_D; + logic Start_S; + logic [C_RM-1:0] RM_dly_S; + logic Div_enable_S; + logic Sqrt_enable_S; + logic Inf_a_S; + logic Inf_b_S; + logic Zero_a_S; + logic Zero_b_S; + logic NaN_a_S; + logic NaN_b_S; + logic SNaN_S; + logic Special_case_SB,Special_case_dly_SB; + + logic Full_precision_S; + logic FP32_S; + logic FP64_S; + logic FP16_S; + logic FP16ALT_S; + + + preprocess_mvp preprocess_U0 + ( + .Clk_CI (Clk_CI ), + .Rst_RBI (Rst_RBI ), + .Div_start_SI (Div_start_SI ), + .Sqrt_start_SI (Sqrt_start_SI ), + .Ready_SI (Ready_SO ), + .Operand_a_DI (Operand_a_DI ), + .Operand_b_DI (Operand_b_DI ), + .RM_SI (RM_SI ), + .Format_sel_SI (Format_sel_SI ), + .Start_SO (Start_S ), + .Exp_a_DO_norm (Exp_a_D ), + .Exp_b_DO_norm (Exp_b_D ), + .Mant_a_DO_norm (Mant_a_D ), + .Mant_b_DO_norm (Mant_b_D ), + .RM_dly_SO (RM_dly_S ), + .Sign_z_DO (Sign_z_D ), + .Inf_a_SO (Inf_a_S ), + .Inf_b_SO (Inf_b_S ), + .Zero_a_SO (Zero_a_S ), + .Zero_b_SO (Zero_b_S ), + .NaN_a_SO (NaN_a_S ), + .NaN_b_SO (NaN_b_S ), + .SNaN_SO (SNaN_S ), + .Special_case_SBO (Special_case_SB ), + .Special_case_dly_SBO (Special_case_dly_SB) + ); + + nrbd_nrsc_mvp nrbd_nrsc_U0 + ( + .Clk_CI (Clk_CI ), + .Rst_RBI (Rst_RBI ), + .Div_start_SI (Div_start_SI ) , + .Sqrt_start_SI (Sqrt_start_SI ), + .Start_SI (Start_S ), + .Kill_SI (Kill_SI ), + .Special_case_SBI (Special_case_SB ), + .Special_case_dly_SBI (Special_case_dly_SB), + .Div_enable_SO (Div_enable_S ), + .Sqrt_enable_SO (Sqrt_enable_S ), + .Precision_ctl_SI (Precision_ctl_SI ), + .Format_sel_SI (Format_sel_SI ), + .Exp_a_DI (Exp_a_D ), + .Exp_b_DI (Exp_b_D ), + .Mant_a_DI (Mant_a_D ), + .Mant_b_DI (Mant_b_D ), + .Full_precision_SO (Full_precision_S ), + .FP32_SO (FP32_S ), + .FP64_SO (FP64_S ), + .FP16_SO (FP16_S ), + .FP16ALT_SO (FP16ALT_S ), + .Ready_SO (Ready_SO ), + .Done_SO (Done_SO ), + .Exp_z_DO (Exp_z_D ), + .Mant_z_DO (Mant_z_D ) + ); + + + norm_div_sqrt_mvp fpu_norm_U0 + ( + .Mant_in_DI (Mant_z_D ), + .Exp_in_DI (Exp_z_D ), + .Sign_in_DI (Sign_z_D ), + .Div_enable_SI (Div_enable_S ), + .Sqrt_enable_SI (Sqrt_enable_S ), + .Inf_a_SI (Inf_a_S ), + .Inf_b_SI (Inf_b_S ), + .Zero_a_SI (Zero_a_S ), + .Zero_b_SI (Zero_b_S ), + .NaN_a_SI (NaN_a_S ), + .NaN_b_SI (NaN_b_S ), + .SNaN_SI (SNaN_S ), + .RM_SI (RM_dly_S ), + .Full_precision_SI (Full_precision_S ), + .FP32_SI (FP32_S ), + .FP64_SI (FP64_S ), + .FP16_SI (FP16_S ), + .FP16ALT_SI (FP16ALT_S ), + .Result_DO (Result_DO ), + .Fflags_SO (Fflags_SO ) //{NV,DZ,OF,UF,NX} + ); + +endmodule
diff --git a/verilog/rtl/dm_csrs.sv b/verilog/rtl/dm_csrs.sv new file mode 100644 index 0000000..e74d320 --- /dev/null +++ b/verilog/rtl/dm_csrs.sv
@@ -0,0 +1,630 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the “License”); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * File: dm_csrs.sv + * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> + * Date: 30.6.2018 + * + * Description: Debug CSRs. Communication over Debug Transport Module (DTM) + */ + +module dm_csrs #( + parameter int unsigned NrHarts = 1, + parameter int unsigned BusWidth = 32, + parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}} +) ( + input logic clk_i, // Clock + input logic rst_ni, // Asynchronous reset active low + input logic testmode_i, + input logic dmi_rst_ni, // Debug Module Intf reset active-low + input logic dmi_req_valid_i, + output logic dmi_req_ready_o, + input dm::dmi_req_t dmi_req_i, + // every request needs a response one cycle later + output logic dmi_resp_valid_o, + input logic dmi_resp_ready_i, + output dm::dmi_resp_t dmi_resp_o, + // global ctrl + output logic ndmreset_o, // non-debug module reset active-high + output logic dmactive_o, // 1 -> debug-module is active, + // 0 -> synchronous re-set + // hart status + input dm::hartinfo_t [NrHarts-1:0] hartinfo_i, // static hartinfo + input logic [NrHarts-1:0] halted_i, // hart is halted + input logic [NrHarts-1:0] unavailable_i, // e.g.: powered down + input logic [NrHarts-1:0] resumeack_i, // hart acknowledged resume request + // hart control + output logic [19:0] hartsel_o, // hartselect to ctrl module + output logic [NrHarts-1:0] haltreq_o, // request to halt a hart + output logic [NrHarts-1:0] resumereq_o, // request hart to resume + output logic clear_resumeack_o, + + output logic cmd_valid_o, // debugger writing to cmd field + output dm::command_t cmd_o, // abstract command + input logic cmderror_valid_i, // an error occurred + input dm::cmderr_e cmderror_i, // this error occurred + input logic cmdbusy_i, // cmd is currently busy executing + + output logic [dm::ProgBufSize-1:0][31:0] progbuf_o, // to system bus + output logic [dm::DataCount-1:0][31:0] data_o, + + input logic [dm::DataCount-1:0][31:0] data_i, + input logic data_valid_i, + // system bus access module (SBA) + output logic [BusWidth-1:0] sbaddress_o, + input logic [BusWidth-1:0] sbaddress_i, + output logic sbaddress_write_valid_o, + // control signals in + output logic sbreadonaddr_o, + output logic sbautoincrement_o, + output logic [2:0] sbaccess_o, + // data out + output logic sbreadondata_o, + output logic [BusWidth-1:0] sbdata_o, + output logic sbdata_read_valid_o, + output logic sbdata_write_valid_o, + // read data in + input logic [BusWidth-1:0] sbdata_i, + input logic sbdata_valid_i, + // control signals + input logic sbbusy_i, + input logic sberror_valid_i, // bus error occurred + input logic [2:0] sberror_i // bus error occurred +); + + // the amount of bits we need to represent all harts + localparam int unsigned HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); + localparam int unsigned NrHartsAligned = 2**HartSelLen; + + dm::dtm_op_e dtm_op; + assign dtm_op = dm::dtm_op_e'(dmi_req_i.op); + + logic [31:0] resp_queue_data; + + localparam dm::dm_csr_e DataEnd = dm::dm_csr_e'(dm::Data0 + {4'b0, dm::DataCount} - 8'h1); + localparam dm::dm_csr_e ProgBufEnd = dm::dm_csr_e'(dm::ProgBuf0 + {4'b0, dm::ProgBufSize} - 8'h1); + + logic [31:0] haltsum0, haltsum1, haltsum2, haltsum3; + logic [((NrHarts-1)/2**5 + 1) * 32 - 1 : 0] halted; + logic [(NrHarts-1)/2**5:0][31:0] halted_reshaped0; + logic [(NrHarts-1)/2**10:0][31:0] halted_reshaped1; + logic [(NrHarts-1)/2**15:0][31:0] halted_reshaped2; + logic [((NrHarts-1)/2**10+1)*32-1:0] halted_flat1; + logic [((NrHarts-1)/2**15+1)*32-1:0] halted_flat2; + logic [31:0] halted_flat3; + + // haltsum0 + logic [14:0] hartsel_idx0; + always_comb begin : p_haltsum0 + halted = '0; + haltsum0 = '0; + hartsel_idx0 = hartsel_o[19:5]; + halted[NrHarts-1:0] = halted_i; + halted_reshaped0 = halted; + if (hartsel_idx0 < 15'((NrHarts-1)/2**5+1)) begin + haltsum0 = halted_reshaped0[hartsel_idx0]; + end + end + + // haltsum1 + logic [9:0] hartsel_idx1; + always_comb begin : p_reduction1 + halted_flat1 = '0; + haltsum1 = '0; + hartsel_idx1 = hartsel_o[19:10]; + + for (int unsigned k = 0; k < (NrHarts-1)/2**5+1; k++) begin + halted_flat1[k] = |halted_reshaped0[k]; + end + halted_reshaped1 = halted_flat1; + + if (hartsel_idx1 < 10'(((NrHarts-1)/2**10+1))) begin + haltsum1 = halted_reshaped1[hartsel_idx1]; + end + end + + // haltsum2 + logic [4:0] hartsel_idx2; + always_comb begin : p_reduction2 + halted_flat2 = '0; + haltsum2 = '0; + hartsel_idx2 = hartsel_o[19:15]; + + for (int unsigned k = 0; k < (NrHarts-1)/2**10+1; k++) begin + halted_flat2[k] = |halted_reshaped1[k]; + end + halted_reshaped2 = halted_flat2; + + if (hartsel_idx2 < 5'(((NrHarts-1)/2**15+1))) begin + haltsum2 = halted_reshaped2[hartsel_idx2]; + end + end + + // haltsum3 + always_comb begin : p_reduction3 + halted_flat3 = '0; + for (int unsigned k = 0; k < NrHarts/2**15+1; k++) begin + halted_flat3[k] = |halted_reshaped2[k]; + end + haltsum3 = halted_flat3; + end + + + dm::dmstatus_t dmstatus; + dm::dmcontrol_t dmcontrol_d, dmcontrol_q; + dm::abstractcs_t abstractcs; + dm::cmderr_e cmderr_d, cmderr_q; + dm::command_t command_d, command_q; + logic cmd_valid_d, cmd_valid_q; + dm::abstractauto_t abstractauto_d, abstractauto_q; + dm::sbcs_t sbcs_d, sbcs_q; + logic [63:0] sbaddr_d, sbaddr_q; + logic [63:0] sbdata_d, sbdata_q; + + logic [NrHarts-1:0] havereset_d, havereset_q; + // program buffer + logic [dm::ProgBufSize-1:0][31:0] progbuf_d, progbuf_q; + logic [dm::DataCount-1:0][31:0] data_d, data_q; + + logic [HartSelLen-1:0] selected_hart; + + // a successful response returns zero + assign dmi_resp_o.resp = dm::DTM_SUCCESS; + // SBA + assign sbautoincrement_o = sbcs_q.sbautoincrement; + assign sbreadonaddr_o = sbcs_q.sbreadonaddr; + assign sbreadondata_o = sbcs_q.sbreadondata; + assign sbaccess_o = sbcs_q.sbaccess; + assign sbdata_o = sbdata_q[BusWidth-1:0]; + assign sbaddress_o = sbaddr_q[BusWidth-1:0]; + + assign hartsel_o = {dmcontrol_q.hartselhi, dmcontrol_q.hartsello}; + + // needed to avoid lint warnings + logic [NrHartsAligned-1:0] havereset_d_aligned, havereset_q_aligned, + resumeack_aligned, unavailable_aligned, + halted_aligned; + assign resumeack_aligned = NrHartsAligned'(resumeack_i); + assign unavailable_aligned = NrHartsAligned'(unavailable_i); + assign halted_aligned = NrHartsAligned'(halted_i); + + assign havereset_d = NrHarts'(havereset_d_aligned); + assign havereset_q_aligned = NrHartsAligned'(havereset_q); + + dm::hartinfo_t [NrHartsAligned-1:0] hartinfo_aligned; + always_comb begin : p_hartinfo_align + hartinfo_aligned = '0; + hartinfo_aligned[NrHarts-1:0] = hartinfo_i; + end + + // helper variables + dm::sbcs_t sbcs; + dm::dmcontrol_t dmcontrol; + dm::abstractcs_t a_abstractcs; + logic [4:0] autoexecdata_idx; + always_comb begin : csr_read_write + // -------------------- + // Static Values (R/O) + // -------------------- + // dmstatus + dmstatus = '0; + dmstatus.version = dm::DbgVersion013; + // no authentication implemented + dmstatus.authenticated = 1'b1; + // we do not support halt-on-reset sequence + dmstatus.hasresethaltreq = 1'b0; + // TODO(zarubaf) things need to change here if we implement the array mask + dmstatus.allhavereset = havereset_q_aligned[selected_hart]; + dmstatus.anyhavereset = havereset_q_aligned[selected_hart]; + + dmstatus.allresumeack = resumeack_aligned[selected_hart]; + dmstatus.anyresumeack = resumeack_aligned[selected_hart]; + + dmstatus.allunavail = unavailable_aligned[selected_hart]; + dmstatus.anyunavail = unavailable_aligned[selected_hart]; + + // as soon as we are out of the legal Hart region tell the debugger + // that there are only non-existent harts + dmstatus.allnonexistent = logic'(32'(hartsel_o) > (NrHarts - 1)); + dmstatus.anynonexistent = logic'(32'(hartsel_o) > (NrHarts - 1)); + + // We are not allowed to be in multiple states at once. This is a to + // make the running/halted and unavailable states exclusive. + dmstatus.allhalted = halted_aligned[selected_hart] & ~unavailable_aligned[selected_hart]; + dmstatus.anyhalted = halted_aligned[selected_hart] & ~unavailable_aligned[selected_hart]; + + dmstatus.allrunning = ~halted_aligned[selected_hart] & ~unavailable_aligned[selected_hart]; + dmstatus.anyrunning = ~halted_aligned[selected_hart] & ~unavailable_aligned[selected_hart]; + + // abstractcs + abstractcs = '0; + abstractcs.datacount = dm::DataCount; + abstractcs.progbufsize = dm::ProgBufSize; + abstractcs.busy = cmdbusy_i; + abstractcs.cmderr = cmderr_q; + + // abstractautoexec + abstractauto_d = abstractauto_q; + abstractauto_d.zero0 = '0; + + // default assignments + havereset_d_aligned = NrHartsAligned'(havereset_q); + dmcontrol_d = dmcontrol_q; + cmderr_d = cmderr_q; + command_d = command_q; + progbuf_d = progbuf_q; + data_d = data_q; + sbcs_d = sbcs_q; + sbaddr_d = 64'(sbaddress_i); + sbdata_d = sbdata_q; + + resp_queue_data = 32'b0; + cmd_valid_d = 1'b0; + sbaddress_write_valid_o = 1'b0; + sbdata_read_valid_o = 1'b0; + sbdata_write_valid_o = 1'b0; + clear_resumeack_o = 1'b0; + + // helper variables + sbcs = '0; + dmcontrol = '0; + a_abstractcs = '0; + + autoexecdata_idx = dmi_req_i.addr[4:0] - 5'(dm::Data0); + + // localparam int unsigned DataCountAlign = $clog2(dm::DataCount); + // reads + if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_READ) begin + unique case ({1'b0, dmi_req_i.addr}) inside + [(dm::Data0):DataEnd]: begin + // logic [$clog2(dm::DataCount)-1:0] resp_queue_idx; + // resp_queue_idx = dmi_req_i.addr[4:0] - int'(dm::Data0); + resp_queue_data = data_q[$clog2(dm::DataCount)'(autoexecdata_idx)]; + if (!cmdbusy_i) begin + // check whether we need to re-execute the command (just give a cmd_valid) + if (autoexecdata_idx < $bits(abstractauto_q.autoexecdata)) begin + cmd_valid_d = abstractauto_q.autoexecdata[autoexecdata_idx]; + end + end + end + dm::DMControl: resp_queue_data = dmcontrol_q; + dm::DMStatus: resp_queue_data = dmstatus; + dm::Hartinfo: resp_queue_data = hartinfo_aligned[selected_hart]; + dm::AbstractCS: resp_queue_data = abstractcs; + dm::AbstractAuto: resp_queue_data = abstractauto_q; + // command is read-only + dm::Command: resp_queue_data = '0; + [(dm::ProgBuf0):ProgBufEnd]: begin + resp_queue_data = progbuf_q[dmi_req_i.addr[$clog2(dm::ProgBufSize)-1:0]]; + if (!cmdbusy_i) begin + // check whether we need to re-execute the command (just give a cmd_valid) + // range of autoexecprogbuf is 31:16 + cmd_valid_d = abstractauto_q.autoexecprogbuf[{1'b1, dmi_req_i.addr[3:0]}]; + end + end + dm::HaltSum0: resp_queue_data = haltsum0; + dm::HaltSum1: resp_queue_data = haltsum1; + dm::HaltSum2: resp_queue_data = haltsum2; + dm::HaltSum3: resp_queue_data = haltsum3; + dm::SBCS: begin + resp_queue_data = sbcs_q; + end + dm::SBAddress0: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + resp_queue_data = sbaddr_q[31:0]; + end + end + dm::SBAddress1: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + resp_queue_data = sbaddr_q[63:32]; + end + end + dm::SBData0: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + sbdata_read_valid_o = (sbcs_q.sberror == '0); + resp_queue_data = sbdata_q[31:0]; + end + end + dm::SBData1: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + resp_queue_data = sbdata_q[63:32]; + end + end + default:; + endcase + end + + // write + if (dmi_req_ready_o && dmi_req_valid_i && dtm_op == dm::DTM_WRITE) begin + unique case (dm::dm_csr_e'({1'b0, dmi_req_i.addr})) inside + [(dm::Data0):DataEnd]: begin + // attempts to write them while busy is set does not change their value + if (!cmdbusy_i && dm::DataCount > 0) begin + data_d[dmi_req_i.addr[$clog2(dm::DataCount)-1:0]] = dmi_req_i.data; + // check whether we need to re-execute the command (just give a cmd_valid) + if (autoexecdata_idx < $bits(abstractauto_q.autoexecdata)) begin + cmd_valid_d = abstractauto_q.autoexecdata[autoexecdata_idx]; + end + end + end + dm::DMControl: begin + dmcontrol = dm::dmcontrol_t'(dmi_req_i.data); + // clear the havreset of the selected hart + if (dmcontrol.ackhavereset) begin + havereset_d_aligned[selected_hart] = 1'b0; + end + dmcontrol_d = dmi_req_i.data; + end + dm::DMStatus:; // write are ignored to R/O register + dm::Hartinfo:; // hartinfo is R/O + // only command error is write-able + dm::AbstractCS: begin // W1C + // Gets set if an abstract command fails. The bits in this + // field remain set until they are cleared by writing 1 to + // them. No abstract command is started until the value is + // reset to 0. + a_abstractcs = dm::abstractcs_t'(dmi_req_i.data); + // reads during abstract command execution are not allowed + if (!cmdbusy_i) begin + cmderr_d = dm::cmderr_e'(~a_abstractcs.cmderr & cmderr_q); + end else if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end + end + dm::Command: begin + // writes are ignored if a command is already busy + if (!cmdbusy_i) begin + cmd_valid_d = 1'b1; + command_d = dm::command_t'(dmi_req_i.data); + // if there was an attempted to write during a busy execution + // and the cmderror field is zero set the busy error + end else if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end + end + dm::AbstractAuto: begin + // this field can only be written legally when there is no command executing + if (!cmdbusy_i) begin + abstractauto_d = 32'b0; + abstractauto_d.autoexecdata = 12'(dmi_req_i.data[dm::DataCount-1:0]); + abstractauto_d.autoexecprogbuf = 16'(dmi_req_i.data[dm::ProgBufSize-1+16:16]); + end else if (cmderr_q == dm::CmdErrNone) begin + cmderr_d = dm::CmdErrBusy; + end + end + [(dm::ProgBuf0):ProgBufEnd]: begin + // attempts to write them while busy is set does not change their value + if (!cmdbusy_i) begin + progbuf_d[dmi_req_i.addr[$clog2(dm::ProgBufSize)-1:0]] = dmi_req_i.data; + // check whether we need to re-execute the command (just give a cmd_valid) + // this should probably throw an error if executed during another command + // was busy + // range of autoexecprogbuf is 31:16 + cmd_valid_d = abstractauto_q.autoexecprogbuf[{1'b1, dmi_req_i.addr[3:0]}]; + end + end + dm::SBCS: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + sbcs = dm::sbcs_t'(dmi_req_i.data); + sbcs_d = sbcs; + // R/W1C + sbcs_d.sbbusyerror = sbcs_q.sbbusyerror & (~sbcs.sbbusyerror); + sbcs_d.sberror = sbcs_q.sberror & (~sbcs.sberror); + end + end + dm::SBAddress0: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + sbaddr_d[31:0] = dmi_req_i.data; + sbaddress_write_valid_o = (sbcs_q.sberror == '0); + end + end + dm::SBAddress1: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + sbaddr_d[63:32] = dmi_req_i.data; + end + end + dm::SBData0: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + sbdata_d[31:0] = dmi_req_i.data; + sbdata_write_valid_o = (sbcs_q.sberror == '0); + end + end + dm::SBData1: begin + // access while the SBA was busy + if (sbbusy_i) begin + sbcs_d.sbbusyerror = 1'b1; + end else begin + sbdata_d[63:32] = dmi_req_i.data; + end + end + default:; + endcase + end + // hart threw a command error and has precedence over bus writes + if (cmderror_valid_i) begin + cmderr_d = cmderror_i; + end + + // update data registers + if (data_valid_i) begin + data_d = data_i; + end + + // set the havereset flag when we did a ndmreset + if (ndmreset_o) begin + havereset_d_aligned[NrHarts-1:0] = '1; + end + // ------------- + // System Bus + // ------------- + // set bus error + if (sberror_valid_i) begin + sbcs_d.sberror = sberror_i; + end + // update read data + if (sbdata_valid_i) begin + sbdata_d = 64'(sbdata_i); + end + + // dmcontrol + // TODO(zarubaf) we currently do not implement the hartarry mask + dmcontrol_d.hasel = 1'b0; + // we do not support resetting an individual hart + dmcontrol_d.hartreset = 1'b0; + dmcontrol_d.setresethaltreq = 1'b0; + dmcontrol_d.clrresethaltreq = 1'b0; + dmcontrol_d.zero1 = '0; + dmcontrol_d.zero0 = '0; + // Non-writeable, clear only + dmcontrol_d.ackhavereset = 1'b0; + if (!dmcontrol_q.resumereq && dmcontrol_d.resumereq) begin + clear_resumeack_o = 1'b1; + end + if (dmcontrol_q.resumereq && resumeack_i) begin + dmcontrol_d.resumereq = 1'b0; + end + // static values for dcsr + sbcs_d.sbversion = 3'd1; + sbcs_d.sbbusy = sbbusy_i; + sbcs_d.sbasize = $bits(sbcs_d.sbasize)'(BusWidth); + sbcs_d.sbaccess128 = 1'b0; + sbcs_d.sbaccess64 = logic'(BusWidth == 32'd64); + sbcs_d.sbaccess32 = logic'(BusWidth == 32'd32); + sbcs_d.sbaccess16 = 1'b0; + sbcs_d.sbaccess8 = 1'b0; + sbcs_d.sbaccess = (BusWidth == 32'd64) ? 3'd3 : 3'd2; + end + + // output multiplexer + always_comb begin : p_outmux + selected_hart = hartsel_o[HartSelLen-1:0]; + // default assignment + haltreq_o = '0; + resumereq_o = '0; + if (selected_hart < (HartSelLen+1)'(NrHarts)) begin + haltreq_o[selected_hart] = dmcontrol_q.haltreq; + resumereq_o[selected_hart] = dmcontrol_q.resumereq; + end + end + + assign dmactive_o = dmcontrol_q.dmactive; + assign cmd_o = command_q; + assign cmd_valid_o = cmd_valid_q; + assign progbuf_o = progbuf_q; + assign data_o = data_q; + + assign ndmreset_o = dmcontrol_q.ndmreset; + + logic unused_testmode; + assign unused_testmode = testmode_i; + + // response FIFO + fifo_sync #( + .Width (32), + .Pass (1'b0), + .Depth (2) + ) i_fifo ( + .clk_i ( clk_i ), + .rst_ni ( dmi_rst_ni ), // reset only when system is re-set + .clr_i ( 1'b0 ), + .wdata_i ( resp_queue_data ), + .wvalid_i( dmi_req_valid_i ), + .wready_o( dmi_req_ready_o ), + .rdata_o ( dmi_resp_o.data ), + .rvalid_o( dmi_resp_valid_o ), + .rready_i( dmi_resp_ready_i ), + .depth_o ( ) // Doesn't use + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + // PoR + if (!rst_ni) begin + dmcontrol_q <= '0; + // this is the only write-able bit during reset + cmderr_q <= dm::CmdErrNone; + command_q <= '0; + cmd_valid_q <= '0; + abstractauto_q <= '0; + progbuf_q <= '0; + data_q <= '0; + sbcs_q <= '0; + sbaddr_q <= '0; + sbdata_q <= '0; + havereset_q <= '1; + end else begin + havereset_q <= SelectableHarts & havereset_d; + // synchronous re-set of debug module, active-low, except for dmactive + if (!dmcontrol_q.dmactive) begin + dmcontrol_q.haltreq <= '0; + dmcontrol_q.resumereq <= '0; + dmcontrol_q.hartreset <= '0; + dmcontrol_q.ackhavereset <= '0; + dmcontrol_q.zero1 <= '0; + dmcontrol_q.hasel <= '0; + dmcontrol_q.hartsello <= '0; + dmcontrol_q.hartselhi <= '0; + dmcontrol_q.zero0 <= '0; + dmcontrol_q.setresethaltreq <= '0; + dmcontrol_q.clrresethaltreq <= '0; + dmcontrol_q.ndmreset <= '0; + // this is the only write-able bit during reset + dmcontrol_q.dmactive <= dmcontrol_d.dmactive; + cmderr_q <= dm::CmdErrNone; + command_q <= '0; + cmd_valid_q <= '0; + abstractauto_q <= '0; + progbuf_q <= '0; + data_q <= '0; + sbcs_q <= '0; + sbaddr_q <= '0; + sbdata_q <= '0; + end else begin + dmcontrol_q <= dmcontrol_d; + cmderr_q <= cmderr_d; + command_q <= command_d; + cmd_valid_q <= cmd_valid_d; + abstractauto_q <= abstractauto_d; + progbuf_q <= progbuf_d; + data_q <= data_d; + sbcs_q <= sbcs_d; + sbaddr_q <= sbaddr_d; + sbdata_q <= sbdata_d; + end + end + end + + + //pragma translate_on + +endmodule : dm_csrs
diff --git a/verilog/rtl/dm_mem.sv b/verilog/rtl/dm_mem.sv new file mode 100644 index 0000000..4ef7a26 --- /dev/null +++ b/verilog/rtl/dm_mem.sv
@@ -0,0 +1,523 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. +* Copyright and related rights are licensed under the Solderpad Hardware +* License, Version 0.51 (the “License”); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +* or agreed to in writing, software, hardware and materials distributed under +* this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +* CONDITIONS OF ANY KIND, either express or implied. See the License for the +* specific language governing permissions and limitations under the License. +* +* File: dm_mem.sv +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> +* Date: 11.7.2018 +* +* Description: Memory module for execution-based debug clients +* +*/ + +module dm_mem #( + parameter int unsigned NrHarts = 1, + parameter int unsigned BusWidth = 32, + parameter logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}}, + parameter int unsigned DmBaseAddress = '0 +) ( + input logic clk_i, // Clock + input logic rst_ni, // debug module reset + + output logic [NrHarts-1:0] debug_req_o, + input logic [19:0] hartsel_i, + // from Ctrl and Status register + input logic [NrHarts-1:0] haltreq_i, + input logic [NrHarts-1:0] resumereq_i, + input logic clear_resumeack_i, + + // state bits + output logic [NrHarts-1:0] halted_o, // hart acknowledge halt + output logic [NrHarts-1:0] resuming_o, // hart is resuming + + input logic [dm::ProgBufSize-1:0][31:0] progbuf_i, // program buffer to expose + + input logic [dm::DataCount-1:0][31:0] data_i, // data in + output logic [dm::DataCount-1:0][31:0] data_o, // data out + output logic data_valid_o, // data out is valid + // abstract command interface + input logic cmd_valid_i, + input dm::command_t cmd_i, + output logic cmderror_valid_o, + output dm::cmderr_e cmderror_o, + output logic cmdbusy_o, + // data interface + + // SRAM interface + input logic req_i, + input logic we_i, + input logic [BusWidth-1:0] addr_i, + input logic [BusWidth-1:0] wdata_i, + input logic [BusWidth/8-1:0] be_i, + output logic [BusWidth-1:0] rdata_o +); + localparam int unsigned DbgAddressBits = 12; + localparam int unsigned HartSelLen = (NrHarts == 1) ? 1 : $clog2(NrHarts); + localparam int unsigned NrHartsAligned = 2**HartSelLen; + localparam int unsigned MaxAar = (BusWidth == 64) ? 4 : 3; + localparam bit HasSndScratch = (DmBaseAddress != 0); + // Depending on whether we are at the zero page or not we either use `x0` or `x10/a0` + localparam logic [4:0] LoadBaseAddr = (DmBaseAddress == 0) ? 5'd0 : 5'd10; + + localparam logic [DbgAddressBits-1:0] DataBaseAddr = (dm::DataAddr); + localparam logic [DbgAddressBits-1:0] DataEndAddr = (dm::DataAddr + 4*dm::DataCount - 1); + localparam logic [DbgAddressBits-1:0] ProgBufBaseAddr = (dm::DataAddr - 4*dm::ProgBufSize); + localparam logic [DbgAddressBits-1:0] ProgBufEndAddr = (dm::DataAddr - 1); + localparam logic [DbgAddressBits-1:0] AbstractCmdBaseAddr = (ProgBufBaseAddr - 4*10); + localparam logic [DbgAddressBits-1:0] AbstractCmdEndAddr = (ProgBufBaseAddr - 1); + + localparam logic [DbgAddressBits-1:0] WhereToAddr = 'h300; + localparam logic [DbgAddressBits-1:0] FlagsBaseAddr = 'h400; + localparam logic [DbgAddressBits-1:0] FlagsEndAddr = 'h7FF; + + localparam logic [DbgAddressBits-1:0] HaltedAddr = 'h100; + localparam logic [DbgAddressBits-1:0] GoingAddr = 'h104; + localparam logic [DbgAddressBits-1:0] ResumingAddr = 'h108; + localparam logic [DbgAddressBits-1:0] ExceptionAddr = 'h10C; + + logic [dm::ProgBufSize/2-1:0][63:0] progbuf; + logic [7:0][63:0] abstract_cmd; + logic [NrHarts-1:0] halted_d, halted_q; + logic [NrHarts-1:0] resuming_d, resuming_q; + logic resume, go, going; + + logic exception; + logic unsupported_command; + + logic [63:0] rom_rdata; + logic [63:0] rdata_d, rdata_q; + logic word_enable32_q; + + // this is needed to avoid lint warnings related to array indexing + // resize hartsel to valid range + logic [HartSelLen-1:0] hartsel, wdata_hartsel; + + assign hartsel = hartsel_i[HartSelLen-1:0]; + assign wdata_hartsel = wdata_i[HartSelLen-1:0]; + + logic [NrHartsAligned-1:0] resumereq_aligned, haltreq_aligned, + halted_d_aligned, halted_q_aligned, + halted_aligned, resumereq_wdata_aligned, + resuming_d_aligned, resuming_q_aligned; + + assign resumereq_aligned = NrHartsAligned'(resumereq_i); + assign haltreq_aligned = NrHartsAligned'(haltreq_i); + assign resumereq_wdata_aligned = NrHartsAligned'(resumereq_i); + + assign halted_q_aligned = NrHartsAligned'(halted_q); + assign halted_d = NrHarts'(halted_d_aligned); + assign resuming_q_aligned = NrHartsAligned'(resuming_q); + assign resuming_d = NrHarts'(resuming_d_aligned); + + // distinguish whether we need to forward data from the ROM or the FSM + // latch the address for this + logic fwd_rom_d, fwd_rom_q; + dm::ac_ar_cmd_t ac_ar; + + // Abstract Command Access Register + assign ac_ar = dm::ac_ar_cmd_t'(cmd_i.control); + assign debug_req_o = haltreq_i; + assign halted_o = halted_q; + assign resuming_o = resuming_q; + + // reshape progbuf + assign progbuf = progbuf_i; + + typedef enum logic [1:0] { Idle, Go, Resume, CmdExecuting } state_e; + state_e state_d, state_q; + + // hart ctrl queue + always_comb begin : p_hart_ctrl_queue + cmderror_valid_o = 1'b0; + cmderror_o = dm::CmdErrNone; + state_d = state_q; + go = 1'b0; + resume = 1'b0; + cmdbusy_o = 1'b1; + + unique case (state_q) + Idle: begin + cmdbusy_o = 1'b0; + if (cmd_valid_i && halted_q_aligned[hartsel] && !unsupported_command) begin + // give the go signal + state_d = Go; + end else if (cmd_valid_i) begin + // hart must be halted for all requests + cmderror_valid_o = 1'b1; + cmderror_o = dm::CmdErrorHaltResume; + end + // CSRs want to resume, the request is ignored when the hart is + // requested to halt or it didn't clear the resuming_q bit before + if (resumereq_aligned[hartsel] && !resuming_q_aligned[hartsel] && + !haltreq_aligned[hartsel] && halted_q_aligned[hartsel]) begin + state_d = Resume; + end + end + + Go: begin + // we are already busy here since we scheduled the execution of a program + cmdbusy_o = 1'b1; + go = 1'b1; + // the thread is now executing the command, track its state + if (going) begin + state_d = CmdExecuting; + end + end + + Resume: begin + cmdbusy_o = 1'b1; + resume = 1'b1; + if (resuming_q_aligned[hartsel]) begin + state_d = Idle; + end + end + + CmdExecuting: begin + cmdbusy_o = 1'b1; + go = 1'b0; + // wait until the hart has halted again + if (halted_aligned[hartsel]) begin + state_d = Idle; + end + end + + //default: ; + endcase + + // only signal once that cmd is unsupported so that we can clear cmderr + // in subsequent writes to abstractcs + if (unsupported_command && cmd_valid_i) begin + cmderror_valid_o = 1'b1; + cmderror_o = dm::CmdErrNotSupported; + end + + if (exception) begin + cmderror_valid_o = 1'b1; + cmderror_o = dm::CmdErrorException; + end + end + + // word mux for 32bit and 64bit buses + logic [63:0] word_mux; + assign word_mux = (fwd_rom_q) ? rom_rdata : rdata_q; + + if (BusWidth == 64) begin : gen_word_mux64 + assign rdata_o = word_mux; + end else begin : gen_word_mux32 + assign rdata_o = (word_enable32_q) ? word_mux[32 +: 32] : word_mux[0 +: 32]; + end + + // read/write logic + logic [63:0] data_bits; + logic [7:0][7:0] rdata; + always_comb begin : p_rw_logic + + halted_d_aligned = NrHartsAligned'(halted_q); + resuming_d_aligned = NrHartsAligned'(resuming_q); + rdata_d = rdata_q; + // convert the data in bits representation + data_bits = data_i; + rdata = '0; + + // write data in csr register + data_valid_o = 1'b0; + exception = 1'b0; + halted_aligned = '0; + going = 1'b0; + + // The resume ack signal is lowered when the resume request is deasserted + if (clear_resumeack_i) begin + resuming_d_aligned[hartsel] = 1'b0; + end + // we've got a new request + if (req_i) begin + // this is a write + if (we_i) begin + unique case (addr_i[DbgAddressBits-1:0]) inside + HaltedAddr: begin + halted_aligned[wdata_hartsel] = 1'b1; + halted_d_aligned[wdata_hartsel] = 1'b1; + end + GoingAddr: begin + going = 1'b1; + end + ResumingAddr: begin + // clear the halted flag as the hart resumed execution + halted_d_aligned[wdata_hartsel] = 1'b0; + // set the resuming flag which needs to be cleared by the debugger + resuming_d_aligned[wdata_hartsel] = 1'b1; + end + // an exception occurred during execution + ExceptionAddr: exception = 1'b1; + // core can write data registers + [DataBaseAddr:DataEndAddr]: begin + data_valid_o = 1'b1; + for (int i = 0; i < $bits(be_i); i++) begin + if (be_i[i]) begin + data_bits[i*8+:8] = wdata_i[i*8+:8]; + end + end + end + default ; + endcase + + // this is a read + end else begin + unique case (addr_i[DbgAddressBits-1:0]) inside + // variable ROM content + WhereToAddr: begin + // variable jump to abstract cmd, program_buffer or resume + if (resumereq_wdata_aligned[wdata_hartsel]) begin + rdata_d = {32'b0, dm::jal('0, 21'(dm::ResumeAddress[11:0])-21'(WhereToAddr))}; + end + + // there is a command active so jump there + if (cmdbusy_o) begin + // transfer not set is shortcut to the program buffer if postexec is set + // keep this statement narrow to not catch invalid commands + if (cmd_i.cmdtype == dm::AccessRegister && + !ac_ar.transfer && ac_ar.postexec) begin + rdata_d = {32'b0, dm::jal('0, 21'(ProgBufBaseAddr)-21'(WhereToAddr))}; + // this is a legit abstract cmd -> execute it + end else begin + rdata_d = {32'b0, dm::jal('0, 21'(AbstractCmdBaseAddr)-21'(WhereToAddr))}; + end + end + end + + [DataBaseAddr:DataEndAddr]: begin + rdata_d = { + data_i[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - + DataBaseAddr[DbgAddressBits-1:3] + 1'b1)], + data_i[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - + DataBaseAddr[DbgAddressBits-1:3])] + }; + end + + [ProgBufBaseAddr:ProgBufEndAddr]: begin + rdata_d = progbuf[$clog2(dm::ProgBufSize)'(addr_i[DbgAddressBits-1:3] - + ProgBufBaseAddr[DbgAddressBits-1:3])]; + end + + // two slots for abstract command + [AbstractCmdBaseAddr:AbstractCmdEndAddr]: begin + // return the correct address index + rdata_d = abstract_cmd[3'(addr_i[DbgAddressBits-1:3] - + AbstractCmdBaseAddr[DbgAddressBits-1:3])]; + end + // harts are polling for flags here + [FlagsBaseAddr:FlagsEndAddr]: begin + // release the corresponding hart + if (({addr_i[DbgAddressBits-1:3], 3'b0} - FlagsBaseAddr[DbgAddressBits-1:0]) == + (DbgAddressBits'(hartsel) & {{(DbgAddressBits-3){1'b1}}, 3'b0})) begin + rdata[DbgAddressBits'(hartsel) & DbgAddressBits'(3'b111)] = {6'b0, resume, go}; + end + rdata_d = rdata; + end + default: ; + endcase + end + end + + data_o = data_bits; + end + + always_comb begin : p_abstract_cmd_rom + // this abstract command is currently unsupported + unsupported_command = 1'b0; + // default memory + // if ac_ar.transfer is not set then we can take a shortcut to the program buffer + abstract_cmd[0][31:0] = dm::illegal(); + // load debug module base address into a0, this is shared among all commands + abstract_cmd[0][63:32] = HasSndScratch ? dm::auipc(5'd10, '0) : dm::nop(); + // clr lowest 12b -> DM base offset + abstract_cmd[1][31:0] = HasSndScratch ? dm::srli(5'd10, 5'd10, 6'd12) : dm::nop(); + abstract_cmd[1][63:32] = HasSndScratch ? dm::slli(5'd10, 5'd10, 6'd12) : dm::nop(); + abstract_cmd[2][31:0] = dm::nop(); + abstract_cmd[2][63:32] = dm::nop(); + abstract_cmd[3][31:0] = dm::nop(); + abstract_cmd[3][63:32] = dm::nop(); + abstract_cmd[4][31:0] = HasSndScratch ? dm::csrr(dm::CSR_DSCRATCH1, 5'd10) : dm::nop(); + abstract_cmd[4][63:32] = dm::ebreak(); + abstract_cmd[7:5] = '0; + + // this depends on the command being executed + unique case (cmd_i.cmdtype) + // -------------------- + // Access Register + // -------------------- + dm::AccessRegister: begin + if (32'(ac_ar.aarsize) < MaxAar && ac_ar.transfer && ac_ar.write) begin + // store a0 in dscratch1 + abstract_cmd[0][31:0] = HasSndScratch ? dm::csrr(dm::CSR_DSCRATCH1, 5'd10) : dm::nop(); + // this range is reserved + if (ac_ar.regno[15:14] != '0) begin + abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap + unsupported_command = 1'b1; + // A0 access needs to be handled separately, as we use A0 to load + // the DM address offset need to access DSCRATCH1 in this case + end else if (HasSndScratch && ac_ar.regno[12] && (!ac_ar.regno[5]) && + (ac_ar.regno[4:0] == 5'd10)) begin + // store s0 in dscratch + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); + // load from data register + abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, LoadBaseAddr, dm::DataAddr); + // and store it in the corresponding CSR + abstract_cmd[3][31:0] = dm::csrw(dm::CSR_DSCRATCH1, 5'd8); + // restore s0 again from dscratch + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); + // GPR/FPR access + end else if (ac_ar.regno[12]) begin + // determine whether we want to access the floating point register or not + if (ac_ar.regno[5]) begin + abstract_cmd[2][31:0] = + dm::float_load(ac_ar.aarsize, ac_ar.regno[4:0], LoadBaseAddr, dm::DataAddr); + end else begin + abstract_cmd[2][31:0] = + dm::load(ac_ar.aarsize, ac_ar.regno[4:0], LoadBaseAddr, dm::DataAddr); + end + // CSR access + end else begin + // data register to CSR + // store s0 in dscratch + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); + // load from data register + abstract_cmd[2][63:32] = dm::load(ac_ar.aarsize, 5'd8, LoadBaseAddr, dm::DataAddr); + // and store it in the corresponding CSR + abstract_cmd[3][31:0] = dm::csrw(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8); + // restore s0 again from dscratch + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); + end + end else if (32'(ac_ar.aarsize) < MaxAar && ac_ar.transfer && !ac_ar.write) begin + // store a0 in dscratch1 + abstract_cmd[0][31:0] = HasSndScratch ? + dm::csrr(dm::CSR_DSCRATCH1, LoadBaseAddr) : + dm::nop(); + // this range is reserved + if (ac_ar.regno[15:14] != '0) begin + abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap + unsupported_command = 1'b1; + // A0 access needs to be handled separately, as we use A0 to load + // the DM address offset need to access DSCRATCH1 in this case + end else if (HasSndScratch && ac_ar.regno[12] && (!ac_ar.regno[5]) && + (ac_ar.regno[4:0] == 5'd10)) begin + // store s0 in dscratch + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); + // read value from CSR into s0 + abstract_cmd[2][63:32] = dm::csrr(dm::CSR_DSCRATCH1, 5'd8); + // and store s0 into data section + abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, LoadBaseAddr, dm::DataAddr); + // restore s0 again from dscratch + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); + // GPR/FPR access + end else if (ac_ar.regno[12]) begin + // determine whether we want to access the floating point register or not + if (ac_ar.regno[5]) begin + abstract_cmd[2][31:0] = + dm::float_store(ac_ar.aarsize, ac_ar.regno[4:0], LoadBaseAddr, dm::DataAddr); + end else begin + abstract_cmd[2][31:0] = + dm::store(ac_ar.aarsize, ac_ar.regno[4:0], LoadBaseAddr, dm::DataAddr); + end + // CSR access + end else begin + // CSR register to data + // store s0 in dscratch + abstract_cmd[2][31:0] = dm::csrw(dm::CSR_DSCRATCH0, 5'd8); + // read value from CSR into s0 + abstract_cmd[2][63:32] = dm::csrr(dm::csr_reg_t'(ac_ar.regno[11:0]), 5'd8); + // and store s0 into data section + abstract_cmd[3][31:0] = dm::store(ac_ar.aarsize, 5'd8, LoadBaseAddr, dm::DataAddr); + // restore s0 again from dscratch + abstract_cmd[3][63:32] = dm::csrr(dm::CSR_DSCRATCH0, 5'd8); + end + end else if (32'(ac_ar.aarsize) >= MaxAar || ac_ar.aarpostincrement == 1'b1) begin + // this should happend when e.g. ac_ar.aarsize >= MaxAar + // Openocd will try to do an access with aarsize=64 bits + // first before falling back to 32 bits. + abstract_cmd[0][31:0] = dm::ebreak(); // we leave asap + unsupported_command = 1'b1; + end + + // Check whether we need to execute the program buffer. When we + // get an unsupported command we really should abort instead of + // still trying to execute the program buffer, makes it easier + // for the debugger to recover + if (ac_ar.postexec && !unsupported_command) begin + // issue a nop, we will automatically run into the program buffer + abstract_cmd[4][63:32] = dm::nop(); + end + end + // not supported at the moment + // dm::QuickAccess:; + // dm::AccessMemory:; + default: begin + abstract_cmd[0][31:0] = dm::ebreak(); + unsupported_command = 1'b1; + end + endcase + end + + logic [63:0] rom_addr; + assign rom_addr = 64'(addr_i); + + // Depending on whether the debug module is located + // at the zero page we can instantiate a simplified version + // which only requires one scratch register per hart. + // For all other cases we need to set aside + // two registers per hart, hence we also need + // two scratch registers. + if (HasSndScratch) begin : gen_rom_snd_scratch + debug_rom i_debug_rom ( + .clk_i, + .req_i, + .addr_i ( rom_addr ), + .rdata_o ( rom_rdata ) + ); + end else begin : gen_rom_one_scratch + // It uses the zero register (`x0`) as the base + // for its loads. The zero register does not need to + // be saved. + debug_rom_one_scratch i_debug_rom ( + .clk_i, + .req_i, + .addr_i ( rom_addr ), + .rdata_o ( rom_rdata ) + ); + end + + // ROM starts at the HaltAddress of the core e.g.: it immediately jumps to + // the ROM base address + assign fwd_rom_d = logic'(addr_i[DbgAddressBits-1:0] >= dm::HaltAddress[DbgAddressBits-1:0]); + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + fwd_rom_q <= 1'b0; + rdata_q <= '0; + state_q <= Idle; + word_enable32_q <= 1'b0; + end else begin + fwd_rom_q <= fwd_rom_d; + rdata_q <= rdata_d; + state_q <= state_d; + word_enable32_q <= addr_i[2]; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + halted_q <= 1'b0; + resuming_q <= 1'b0; + end else begin + halted_q <= SelectableHarts & halted_d; + resuming_q <= SelectableHarts & resuming_d; + end + end + +endmodule : dm_mem
diff --git a/verilog/rtl/dm_pkg.sv b/verilog/rtl/dm_pkg.sv new file mode 100644 index 0000000..1b7d0f5 --- /dev/null +++ b/verilog/rtl/dm_pkg.sv
@@ -0,0 +1,414 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the “License”); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * File: dm_pkg.sv + * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> + * Date: 30.6.2018 + * + * Description: Debug-module package, contains common system definitions. + * + */ + +package dm; + localparam logic [3:0] DbgVersion013 = 4'h2; + // size of program buffer in junks of 32-bit words + localparam logic [4:0] ProgBufSize = 5'h8; + + // amount of data count registers implemented + localparam logic [3:0] DataCount = 4'h2; + + // address to which a hart should jump when it was requested to halt + localparam logic [63:0] HaltAddress = 64'h800; + localparam logic [63:0] ResumeAddress = HaltAddress + 4; + localparam logic [63:0] ExceptionAddress = HaltAddress + 8; + + // address where data0-15 is shadowed or if shadowed in a CSR + // address of the first CSR used for shadowing the data + localparam logic [11:0] DataAddr = 12'h380; // we are aligned with Rocket here + + // debug registers + typedef enum logic [7:0] { + Data0 = 8'h04, + Data1 = 8'h05, + Data2 = 8'h06, + Data3 = 8'h07, + Data4 = 8'h08, + Data5 = 8'h09, + Data6 = 8'h0A, + Data7 = 8'h0B, + Data8 = 8'h0C, + Data9 = 8'h0D, + Data10 = 8'h0E, + Data11 = 8'h0F, + DMControl = 8'h10, + DMStatus = 8'h11, // r/o + Hartinfo = 8'h12, + HaltSum1 = 8'h13, + HAWindowSel = 8'h14, + HAWindow = 8'h15, + AbstractCS = 8'h16, + Command = 8'h17, + AbstractAuto = 8'h18, + DevTreeAddr0 = 8'h19, + DevTreeAddr1 = 8'h1A, + DevTreeAddr2 = 8'h1B, + DevTreeAddr3 = 8'h1C, + NextDM = 8'h1D, + ProgBuf0 = 8'h20, + ProgBuf15 = 8'h2F, + AuthData = 8'h30, + HaltSum2 = 8'h34, + HaltSum3 = 8'h35, + SBAddress3 = 8'h37, + SBCS = 8'h38, + SBAddress0 = 8'h39, + SBAddress1 = 8'h3A, + SBAddress2 = 8'h3B, + SBData0 = 8'h3C, + SBData1 = 8'h3D, + SBData2 = 8'h3E, + SBData3 = 8'h3F, + HaltSum0 = 8'h40 + } dm_csr_e; + + // debug causes + localparam logic [2:0] CauseBreakpoint = 3'h1; + localparam logic [2:0] CauseTrigger = 3'h2; + localparam logic [2:0] CauseRequest = 3'h3; + localparam logic [2:0] CauseSingleStep = 3'h4; + + typedef struct packed { + logic [31:23] zero1; + logic impebreak; + logic [21:20] zero0; + logic allhavereset; + logic anyhavereset; + logic allresumeack; + logic anyresumeack; + logic allnonexistent; + logic anynonexistent; + logic allunavail; + logic anyunavail; + logic allrunning; + logic anyrunning; + logic allhalted; + logic anyhalted; + logic authenticated; + logic authbusy; + logic hasresethaltreq; + logic devtreevalid; + logic [3:0] version; + } dmstatus_t; + + typedef struct packed { + logic haltreq; + logic resumereq; + logic hartreset; + logic ackhavereset; + logic zero1; + logic hasel; + logic [25:16] hartsello; + logic [15:6] hartselhi; + logic [5:4] zero0; + logic setresethaltreq; + logic clrresethaltreq; + logic ndmreset; + logic dmactive; + } dmcontrol_t; + + typedef struct packed { + logic [31:24] zero1; + logic [23:20] nscratch; + logic [19:17] zero0; + logic dataaccess; + logic [15:12] datasize; + logic [11:0] dataaddr; + } hartinfo_t; + + typedef enum logic [2:0] { + CmdErrNone, CmdErrBusy, CmdErrNotSupported, + CmdErrorException, CmdErrorHaltResume, + CmdErrorBus, CmdErrorOther = 7 + } cmderr_e; + + typedef struct packed { + logic [31:29] zero3; + logic [28:24] progbufsize; + logic [23:13] zero2; + logic busy; + logic zero1; + cmderr_e cmderr; + logic [7:4] zero0; + logic [3:0] datacount; + } abstractcs_t; + + typedef enum logic [7:0] { + AccessRegister = 8'h0, + QuickAccess = 8'h1, + AccessMemory = 8'h2 + } cmd_e; + + typedef struct packed { + cmd_e cmdtype; + logic [23:0] control; + } command_t; + + typedef struct packed { + logic [31:16] autoexecprogbuf; + logic [15:12] zero0; + logic [11:0] autoexecdata; + } abstractauto_t; + + typedef struct packed { + logic zero1; + logic [22:20] aarsize; + logic aarpostincrement; + logic postexec; + logic transfer; + logic write; + logic [15:0] regno; + } ac_ar_cmd_t; + + // DTM + typedef enum logic [1:0] { + DTM_NOP = 2'h0, + DTM_READ = 2'h1, + DTM_WRITE = 2'h2 + } dtm_op_e; + + typedef struct packed { + logic [31:29] sbversion; + logic [28:23] zero0; + logic sbbusyerror; + logic sbbusy; + logic sbreadonaddr; + logic [19:17] sbaccess; + logic sbautoincrement; + logic sbreadondata; + logic [14:12] sberror; + logic [11:5] sbasize; + logic sbaccess128; + logic sbaccess64; + logic sbaccess32; + logic sbaccess16; + logic sbaccess8; + } sbcs_t; + + localparam logic [1:0] DTM_SUCCESS = 2'h0; + + typedef struct packed { + logic [6:0] addr; + dtm_op_e op; + logic [31:0] data; + } dmi_req_t; + + typedef struct packed { + logic [31:0] data; + logic [1:0] resp; + } dmi_resp_t; + + // privilege levels + typedef enum logic[1:0] { + PRIV_LVL_M = 2'b11, + PRIV_LVL_S = 2'b01, + PRIV_LVL_U = 2'b00 + } priv_lvl_t; + + // debugregs in core + typedef struct packed { + logic [31:28] xdebugver; + logic [27:16] zero2; + logic ebreakm; + logic zero1; + logic ebreaks; + logic ebreaku; + logic stepie; + logic stopcount; + logic stoptime; + logic [8:6] cause; + logic zero0; + logic mprven; + logic nmip; + logic step; + priv_lvl_t prv; + } dcsr_t; + + // CSRs + typedef enum logic [11:0] { + // Floating-Point CSRs + CSR_FFLAGS = 12'h001, + CSR_FRM = 12'h002, + CSR_FCSR = 12'h003, + CSR_FTRAN = 12'h800, + // Supervisor Mode CSRs + CSR_SSTATUS = 12'h100, + CSR_SIE = 12'h104, + CSR_STVEC = 12'h105, + CSR_SCOUNTEREN = 12'h106, + CSR_SSCRATCH = 12'h140, + CSR_SEPC = 12'h141, + CSR_SCAUSE = 12'h142, + CSR_STVAL = 12'h143, + CSR_SIP = 12'h144, + CSR_SATP = 12'h180, + // Machine Mode CSRs + CSR_MSTATUS = 12'h300, + CSR_MISA = 12'h301, + CSR_MEDELEG = 12'h302, + CSR_MIDELEG = 12'h303, + CSR_MIE = 12'h304, + CSR_MTVEC = 12'h305, + CSR_MCOUNTEREN = 12'h306, + CSR_MSCRATCH = 12'h340, + CSR_MEPC = 12'h341, + CSR_MCAUSE = 12'h342, + CSR_MTVAL = 12'h343, + CSR_MIP = 12'h344, + CSR_PMPCFG0 = 12'h3A0, + CSR_PMPADDR0 = 12'h3B0, + CSR_MVENDORID = 12'hF11, + CSR_MARCHID = 12'hF12, + CSR_MIMPID = 12'hF13, + CSR_MHARTID = 12'hF14, + CSR_MCYCLE = 12'hB00, + CSR_MINSTRET = 12'hB02, + CSR_DCACHE = 12'h701, + CSR_ICACHE = 12'h700, + + CSR_TSELECT = 12'h7A0, + CSR_TDATA1 = 12'h7A1, + CSR_TDATA2 = 12'h7A2, + CSR_TDATA3 = 12'h7A3, + CSR_TINFO = 12'h7A4, + + // Debug CSR + CSR_DCSR = 12'h7b0, + CSR_DPC = 12'h7b1, + CSR_DSCRATCH0 = 12'h7b2, // optional + CSR_DSCRATCH1 = 12'h7b3, // optional + + // Counters and Timers + CSR_CYCLE = 12'hC00, + CSR_TIME = 12'hC01, + CSR_INSTRET = 12'hC02 + } csr_reg_t; + + + // Instruction Generation Helpers + function automatic logic [31:0] jal (logic [4:0] rd, + logic [20:0] imm); + // OpCode Jal + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h6f}; + endfunction + + function automatic logic [31:0] jalr (logic [4:0] rd, + logic [4:0] rs1, + logic [11:0] offset); + // OpCode Jal + return {offset[11:0], rs1, 3'b0, rd, 7'h67}; + endfunction + + function automatic logic [31:0] andi (logic [4:0] rd, + logic [4:0] rs1, + logic [11:0] imm); + // OpCode andi + return {imm[11:0], rs1, 3'h7, rd, 7'h13}; + endfunction + + function automatic logic [31:0] slli (logic [4:0] rd, + logic [4:0] rs1, + logic [5:0] shamt); + // OpCode slli + return {6'b0, shamt[5:0], rs1, 3'h1, rd, 7'h13}; + endfunction + + function automatic logic [31:0] srli (logic [4:0] rd, + logic [4:0] rs1, + logic [5:0] shamt); + // OpCode srli + return {6'b0, shamt[5:0], rs1, 3'h5, rd, 7'h13}; + endfunction + + function automatic logic [31:0] load (logic [2:0] size, + logic [4:0] dest, + logic [4:0] base, + logic [11:0] offset); + // OpCode Load + return {offset[11:0], base, size, dest, 7'h03}; + endfunction + + function automatic logic [31:0] auipc (logic [4:0] rd, + logic [20:0] imm); + // OpCode Auipc + return {imm[20], imm[10:1], imm[11], imm[19:12], rd, 7'h17}; + endfunction + + function automatic logic [31:0] store (logic [2:0] size, + logic [4:0] src, + logic [4:0] base, + logic [11:0] offset); + // OpCode Store + return {offset[11:5], src, base, size, offset[4:0], 7'h23}; + endfunction + + function automatic logic [31:0] float_load (logic [2:0] size, + logic [4:0] dest, + logic [4:0] base, + logic [11:0] offset); + // OpCode Load + return {offset[11:0], base, size, dest, 7'b00_001_11}; + endfunction + + function automatic logic [31:0] float_store (logic [2:0] size, + logic [4:0] src, + logic [4:0] base, + logic [11:0] offset); + // OpCode Store + return {offset[11:5], src, base, size, offset[4:0], 7'b01_001_11}; + endfunction + + function automatic logic [31:0] csrw (csr_reg_t csr, + logic [4:0] rs1); + // CSRRW, rd, OpCode System + return {csr, rs1, 3'h1, 5'h0, 7'h73}; + endfunction + + function automatic logic [31:0] csrr (csr_reg_t csr, + logic [4:0] dest); + // rs1, CSRRS, rd, OpCode System + return {csr, 5'h0, 3'h2, dest, 7'h73}; + endfunction + + function automatic logic [31:0] branch(logic [4:0] src2, + logic [4:0] src1, + logic [2:0] funct3, + logic [11:0] offset); + // OpCode Branch + return {offset[11], offset[9:4], src2, src1, funct3, + offset[3:0], offset[10], 7'b11_000_11}; + endfunction + + function automatic logic [31:0] ebreak (); + return 32'h00100073; + endfunction + + function automatic logic [31:0] wfi (); + return 32'h10500073; + endfunction + + function automatic logic [31:0] nop (); + return 32'h00000013; + endfunction + + function automatic logic [31:0] illegal (); + return 32'h00000000; + endfunction + +endpackage : dm
diff --git a/verilog/rtl/dm_sba.sv b/verilog/rtl/dm_sba.sv new file mode 100644 index 0000000..c97f956 --- /dev/null +++ b/verilog/rtl/dm_sba.sv
@@ -0,0 +1,172 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. +* Copyright and related rights are licensed under the Solderpad Hardware +* License, Version 0.51 (the “License”); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +* or agreed to in writing, software, hardware and materials distributed under +* this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +* CONDITIONS OF ANY KIND, either express or implied. See the License for the +* specific language governing permissions and limitations under the License. +* +* File: dm_sba.sv +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> +* Date: 1.8.2018 +* +* Description: System Bus Access Module +* +*/ +module dm_sba #( + parameter int unsigned BusWidth = 32 +) ( + input logic clk_i, // Clock + input logic rst_ni, + input logic dmactive_i, // synchronous reset active low + + output logic master_req_o, + output logic [BusWidth-1:0] master_add_o, + output logic master_we_o, + output logic [BusWidth-1:0] master_wdata_o, + output logic [BusWidth/8-1:0] master_be_o, + input logic master_gnt_i, + input logic master_r_valid_i, + input logic [BusWidth-1:0] master_r_rdata_i, + + input logic [BusWidth-1:0] sbaddress_i, + input logic sbaddress_write_valid_i, + // control signals in + input logic sbreadonaddr_i, + output logic [BusWidth-1:0] sbaddress_o, + input logic sbautoincrement_i, + input logic [2:0] sbaccess_i, + // data in + input logic sbreadondata_i, + input logic [BusWidth-1:0] sbdata_i, + input logic sbdata_read_valid_i, + input logic sbdata_write_valid_i, + // read data out + output logic [BusWidth-1:0] sbdata_o, + output logic sbdata_valid_o, + // control signals + output logic sbbusy_o, + output logic sberror_valid_o, // bus error occurred + output logic [2:0] sberror_o // bus error occurred +); + + typedef enum logic [2:0] { Idle, Read, Write, WaitRead, WaitWrite } state_e; + state_e state_d, state_q; + + logic [BusWidth-1:0] address; + logic req; + logic gnt; + logic we; + logic [BusWidth/8-1:0] be; + logic [$clog2(BusWidth/8)-1:0] be_idx; + + assign sbbusy_o = logic'(state_q != Idle); + + always_comb begin : p_fsm + req = 1'b0; + address = sbaddress_i; + we = 1'b0; + be = '0; + be_idx = sbaddress_i[$clog2(BusWidth/8)-1:0]; + + sberror_o = '0; + sberror_valid_o = 1'b0; + sbaddress_o = sbaddress_i; + + state_d = state_q; + + unique case (state_q) + Idle: begin + // debugger requested a read + if (sbaddress_write_valid_i && sbreadonaddr_i) state_d = Read; + // debugger requested a write + if (sbdata_write_valid_i) state_d = Write; + // perform another read + if (sbdata_read_valid_i && sbreadondata_i) state_d = Read; + end + + Read: begin + req = 1'b1; + if (gnt) state_d = WaitRead; + end + + Write: begin + req = 1'b1; + we = 1'b1; + // generate byte enable mask + unique case (sbaccess_i) + 3'b000: begin + be[be_idx] = '1; + end + 3'b001: begin + be[int'({be_idx[$high(be_idx):1], 1'b0}) +: 2] = '1; + end + 3'b010: begin + if (BusWidth == 32'd64) be[int'({be_idx[$high(be_idx)], 2'h0}) +: 4] = '1; + else be = '1; + end + 3'b011: be = '1; + default: ; + endcase + if (gnt) state_d = WaitWrite; + end + + WaitRead: begin + if (sbdata_valid_o) begin + state_d = Idle; + // auto-increment address + if (sbautoincrement_i) sbaddress_o = sbaddress_i + (32'h1 << sbaccess_i); + end + end + + WaitWrite: begin + if (sbdata_valid_o) begin + state_d = Idle; + // auto-increment address + if (sbautoincrement_i) sbaddress_o = sbaddress_i + (32'h1 << sbaccess_i); + end + end + + default: state_d = Idle; // catch parasitic state + endcase + + // handle error case + if (sbaccess_i > 3 && state_q != Idle) begin + req = 1'b0; + state_d = Idle; + sberror_valid_o = 1'b1; + sberror_o = 3'd3; + end + // further error handling should go here ... + end + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs + if (!rst_ni) begin + state_q <= Idle; + end else begin + state_q <= state_d; + end + end + + assign master_req_o = req; + assign master_add_o = address[BusWidth-1:0]; + assign master_we_o = we; + assign master_wdata_o = sbdata_i[BusWidth-1:0]; + assign master_be_o = be[BusWidth/8-1:0]; + assign gnt = master_gnt_i; + assign sbdata_valid_o = master_r_valid_i; + assign sbdata_o = master_r_rdata_i[BusWidth-1:0]; + + + //pragma translate_off + `ifndef VERILATOR + // maybe bump severity to $error if not handled at runtime + dm_sba_access_size: assert property(@(posedge clk_i) disable iff (dmactive_i !== 1'b0) + (state_d != Idle) |-> (sbaccess_i < 4)) + else $warning ("accesses > 8 byte not supported at the moment"); + `endif + //pragma translate_on + +endmodule : dm_sba
diff --git a/verilog/rtl/dmi_cdc.sv b/verilog/rtl/dmi_cdc.sv new file mode 100644 index 0000000..c1c1de2 --- /dev/null +++ b/verilog/rtl/dmi_cdc.sv
@@ -0,0 +1,85 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. +* Copyright and related rights are licensed under the Solderpad Hardware +* License, Version 0.51 (the “License”); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +* or agreed to in writing, software, hardware and materials distributed under +* this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +* CONDITIONS OF ANY KIND, either express or implied. See the License for the +* specific language governing permissions and limitations under the License. +* +* File: axi_riscv_debug_module.sv +* Author: Andreas Traber <atraber@iis.ee.ethz.ch> +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> +* +* Description: Clock domain crossings for JTAG to DMI very heavily based +* on previous work by Andreas Traber for the PULP project. +* This is mainly a wrapper around the existing CDCs. +*/ +module dmi_cdc ( + // JTAG side (master side) + input logic tck_i, + input logic trst_ni, + + input dm::dmi_req_t jtag_dmi_req_i, + output logic jtag_dmi_ready_o, + input logic jtag_dmi_valid_i, + + output dm::dmi_resp_t jtag_dmi_resp_o, + output logic jtag_dmi_valid_o, + input logic jtag_dmi_ready_i, + + // core side (slave side) + input logic clk_i, + input logic rst_ni, + + output dm::dmi_req_t core_dmi_req_o, + output logic core_dmi_valid_o, + input logic core_dmi_ready_i, + + input dm::dmi_resp_t core_dmi_resp_i, + output logic core_dmi_ready_o, + input logic core_dmi_valid_i +); + + // TODO: Make it clean for synthesis. + + fifo_async #( + .Width ( $bits(dm::dmi_req_t) ), + .Depth ( 4 ) + ) i_cdc_req ( + .clk_wr_i ( tck_i ), + .rst_wr_ni ( trst_ni ), + .wvalid_i ( jtag_dmi_valid_i ), + .wready_o ( jtag_dmi_ready_o ), // wrclk + .wdata_i ( jtag_dmi_req_i ), + .wdepth_o ( ), + + .clk_rd_i ( clk_i ), + .rst_rd_ni ( rst_ni ), + .rvalid_o ( core_dmi_valid_o ), + .rready_i ( core_dmi_ready_i ), + .rdata_o ( core_dmi_req_o ), + .rdepth_o ( ) + ); + + fifo_async #( + .Width ( $bits(dm::dmi_resp_t) ), + .Depth ( 4 ) + ) i_cdc_resp ( + .clk_wr_i ( clk_i ), + .rst_wr_ni ( rst_ni ), + .wvalid_i ( core_dmi_valid_i ), + .wready_o ( core_dmi_ready_o ), // wrclk + .wdata_i ( core_dmi_resp_i ), + .wdepth_o ( ), + + .clk_rd_i ( tck_i ), + .rst_rd_ni ( trst_ni ), + .rvalid_o ( jtag_dmi_valid_o ), + .rready_i ( jtag_dmi_ready_i ), + .rdata_o ( jtag_dmi_resp_o ), + .rdepth_o ( ) + ); + +endmodule : dmi_cdc
diff --git a/verilog/rtl/dmi_jtag.sv b/verilog/rtl/dmi_jtag.sv new file mode 100644 index 0000000..917cdc6 --- /dev/null +++ b/verilog/rtl/dmi_jtag.sv
@@ -0,0 +1,264 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. +* Copyright and related rights are licensed under the Solderpad Hardware +* License, Version 0.51 (the “License”); you may not use this file except in +* compliance with the License. You may obtain a copy of the License at +* http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +* or agreed to in writing, software, hardware and materials distributed under +* this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +* CONDITIONS OF ANY KIND, either express or implied. See the License for the +* specific language governing permissions and limitations under the License. +* +* File: axi_riscv_debug_module.sv +* Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> +* Date: 19.7.2018 +* +* Description: JTAG DMI (debug module interface) +* +*/ + +module dmi_jtag #( + parameter logic [31:0] IdcodeValue = 32'h00000001 +) ( + input logic clk_i, // DMI Clock + input logic rst_ni, // Asynchronous reset active low + input logic testmode_i, + + output logic dmi_rst_no, // hard reset + output dm::dmi_req_t dmi_req_o, + output logic dmi_req_valid_o, + input logic dmi_req_ready_i, + + input dm::dmi_resp_t dmi_resp_i, + output logic dmi_resp_ready_o, + input logic dmi_resp_valid_i, + + input logic tck_i, // JTAG test clock pad + input logic tms_i, // JTAG test mode select pad + input logic trst_ni, // JTAG test reset pad + input logic td_i, // JTAG test data input pad + output logic td_o, // JTAG test data output pad + output logic tdo_oe_o // Data out output enable +); + assign dmi_rst_no = rst_ni; + + logic test_logic_reset; + logic shift_dr; + logic update_dr; + logic capture_dr; + logic dmi_access; + logic dtmcs_select; + logic dmi_reset; + logic dmi_tdi; + logic dmi_tdo; + + dm::dmi_req_t dmi_req; + logic dmi_req_ready; + logic dmi_req_valid; + + dm::dmi_resp_t dmi_resp; + logic dmi_resp_valid; + logic dmi_resp_ready; + + typedef struct packed { + logic [6:0] address; + logic [31:0] data; + logic [1:0] op; + } dmi_t; + + typedef enum logic [1:0] { + DMINoError = 2'h0, DMIReservedError = 2'h1, + DMIOPFailed = 2'h2, DMIBusy = 2'h3 + } dmi_error_e; + + typedef enum logic [2:0] { Idle, Read, WaitReadValid, Write, WaitWriteValid } state_e; + state_e state_d, state_q; + + logic [$bits(dmi_t)-1:0] dr_d, dr_q; + logic [6:0] address_d, address_q; + logic [31:0] data_d, data_q; + + dmi_t dmi; + assign dmi = dmi_t'(dr_q); + assign dmi_req.addr = address_q; + assign dmi_req.data = data_q; + assign dmi_req.op = (state_q == Write) ? dm::DTM_WRITE : dm::DTM_READ; + // we'will always be ready to accept the data we requested + assign dmi_resp_ready = 1'b1; + + logic error_dmi_busy; + dmi_error_e error_d, error_q; + + always_comb begin : p_fsm + error_dmi_busy = 1'b0; + // default assignments + state_d = state_q; + address_d = address_q; + data_d = data_q; + error_d = error_q; + + dmi_req_valid = 1'b0; + + unique case (state_q) + Idle: begin + // make sure that no error is sticky + if (dmi_access && update_dr && (error_q == DMINoError)) begin + // save address and value + address_d = dmi.address; + data_d = dmi.data; + if (dm::dtm_op_e'(dmi.op) == dm::DTM_READ) begin + state_d = Read; + end else if (dm::dtm_op_e'(dmi.op) == dm::DTM_WRITE) begin + state_d = Write; + end + // else this is a nop and we can stay here + end + end + + Read: begin + dmi_req_valid = 1'b1; + if (dmi_req_ready) begin + state_d = WaitReadValid; + end + end + + WaitReadValid: begin + // load data into register and shift out + if (dmi_resp_valid) begin + data_d = dmi_resp.data; + state_d = Idle; + end + end + + Write: begin + dmi_req_valid = 1'b1; + // got a valid answer go back to idle + if (dmi_req_ready) begin + state_d = Idle; + end + end + + default: begin + // just wait for idle here + if (dmi_resp_valid) begin + state_d = Idle; + end + end + endcase + + // update_dr means we got another request but we didn't finish + // the one in progress, this state is sticky + if (update_dr && state_q != Idle) begin + error_dmi_busy = 1'b1; + end + + // if capture_dr goes high while we are in the read state + // or in the corresponding wait state we are not giving back a valid word + // -> throw an error + if (capture_dr && state_q inside {Read, WaitReadValid}) begin + error_dmi_busy = 1'b1; + end + + if (error_dmi_busy) begin + error_d = DMIBusy; + end + // clear sticky error flag + if (dmi_reset && dtmcs_select) begin + error_d = DMINoError; + end + end + + // shift register + assign dmi_tdo = dr_q[0]; + + always_comb begin : p_shift + dr_d = dr_q; + + if (capture_dr) begin + if (dmi_access) begin + if (error_q == DMINoError && !error_dmi_busy) begin + dr_d = {address_q, data_q, DMINoError}; + // DMI was busy, report an error + end else if (error_q == DMIBusy || error_dmi_busy) begin + dr_d = {address_q, data_q, DMIBusy}; + end + end + end + + if (shift_dr) begin + if (dmi_access) begin + dr_d = {dmi_tdi, dr_q[$bits(dr_q)-1:1]}; + end + end + + if (test_logic_reset) begin + dr_d = '0; + end + end + + always_ff @(posedge tck_i or negedge trst_ni) begin : p_regs + if (!trst_ni) begin + dr_q <= '0; + state_q <= Idle; + address_q <= '0; + data_q <= '0; + error_q <= DMINoError; + end else begin + dr_q <= dr_d; + state_q <= state_d; + address_q <= address_d; + data_q <= data_d; + error_q <= error_d; + end + end + + // --------- + // TAP + // --------- + dmi_jtag_tap #( + .IrLength (5), + .IdcodeValue(IdcodeValue) + ) i_dmi_jtag_tap ( + .tck_i (tck_i) , + .tms_i (tms_i), + .trst_ni (trst_ni), + .td_i (td_i), + .td_o (td_o), + .tdo_oe_o(tdo_oe_o), + .testmode_i (testmode_i), + .test_logic_reset_o ( test_logic_reset ), + .shift_dr_o ( shift_dr ), + .update_dr_o ( update_dr ), + .capture_dr_o ( capture_dr ), + .dmi_access_o ( dmi_access ), + .dtmcs_select_o ( dtmcs_select ), + .dmi_reset_o ( dmi_reset ), + .dmi_error_i ( error_q ), + .dmi_tdi_o ( dmi_tdi ), + .dmi_tdo_i ( dmi_tdo ) + ); + + // --------- + // CDC + // --------- + dmi_cdc i_dmi_cdc ( + // JTAG side (master side) + .tck_i (tck_i), + .trst_ni (trst_ni), + .jtag_dmi_req_i ( dmi_req ), + .jtag_dmi_ready_o ( dmi_req_ready ), + .jtag_dmi_valid_i ( dmi_req_valid ), + .jtag_dmi_resp_o ( dmi_resp ), + .jtag_dmi_valid_o ( dmi_resp_valid ), + .jtag_dmi_ready_i ( dmi_resp_ready ), + // core side + .clk_i, + .rst_ni, + .core_dmi_req_o ( dmi_req_o ), + .core_dmi_valid_o ( dmi_req_valid_o ), + .core_dmi_ready_i ( dmi_req_ready_i ), + .core_dmi_resp_i ( dmi_resp_i ), + .core_dmi_ready_o ( dmi_resp_ready_o ), + .core_dmi_valid_i ( dmi_resp_valid_i ) + ); + +endmodule : dmi_jtag
diff --git a/verilog/rtl/dmi_jtag_tap.sv b/verilog/rtl/dmi_jtag_tap.sv new file mode 100644 index 0000000..e90f914 --- /dev/null +++ b/verilog/rtl/dmi_jtag_tap.sv
@@ -0,0 +1,345 @@ +/* Copyright 2018 ETH Zurich and University of Bologna. + * Copyright and related rights are licensed under the Solderpad Hardware + * License, Version 0.51 (the “License”); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law + * or agreed to in writing, software, hardware and materials distributed under + * this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * File: dmi_jtag_tap.sv + * Author: Florian Zaruba <zarubaf@iis.ee.ethz.ch> + * Date: 19.7.2018 + * + * Description: JTAG TAP for DMI (according to debug spec 0.13) + * + */ + +module dmi_jtag_tap #( + parameter int unsigned IrLength = 5, + // JTAG IDCODE Value + parameter logic [31:0] IdcodeValue = 32'h00000001 + // xxxx version + // xxxxxxxxxxxxxxxx part number + // xxxxxxxxxxx manufacturer id + // 1 required by standard +) ( + input logic tck_i, // JTAG test clock pad + input logic tms_i, // JTAG test mode select pad + input logic trst_ni, // JTAG test reset pad + input logic td_i, // JTAG test data input pad + output logic td_o, // JTAG test data output pad + output logic tdo_oe_o, // Data out output enable + input logic testmode_i, + output logic test_logic_reset_o, + output logic shift_dr_o, + output logic update_dr_o, + output logic capture_dr_o, + + // we want to access DMI register + output logic dmi_access_o, + // JTAG is interested in writing the DTM CSR register + output logic dtmcs_select_o, + // clear error state + output logic dmi_reset_o, + input logic [1:0] dmi_error_i, + // test data to submodule + output logic dmi_tdi_o, + // test data in from submodule + input logic dmi_tdo_i +); + + // to submodule + assign dmi_tdi_o = td_i; + + typedef enum logic [3:0] { + TestLogicReset, RunTestIdle, SelectDrScan, + CaptureDr, ShiftDr, Exit1Dr, PauseDr, Exit2Dr, + UpdateDr, SelectIrScan, CaptureIr, ShiftIr, + Exit1Ir, PauseIr, Exit2Ir, UpdateIr + } tap_state_e; + + tap_state_e tap_state_q, tap_state_d; + + typedef enum logic [IrLength-1:0] { + BYPASS0 = 'h0, + IDCODE = 'h1, + DTMCSR = 'h10, + DMIACCESS = 'h11, + BYPASS1 = 'h1f + } ir_reg_e; + + typedef struct packed { + logic [31:18] zero1; + logic dmihardreset; + logic dmireset; + logic zero0; + logic [14:12] idle; + logic [11:10] dmistat; + logic [9:4] abits; + logic [3:0] version; + } dtmcs_t; + + // ---------------- + // IR logic + // ---------------- + + // shift register + logic [IrLength-1:0] jtag_ir_shift_d, jtag_ir_shift_q; + // IR register -> this gets captured from shift register upon update_ir + ir_reg_e jtag_ir_d, jtag_ir_q; + logic capture_ir, shift_ir, update_ir; // pause_ir + + always_comb begin : p_jtag + jtag_ir_shift_d = jtag_ir_shift_q; + jtag_ir_d = jtag_ir_q; + + // IR shift register + if (shift_ir) begin + jtag_ir_shift_d = {td_i, jtag_ir_shift_q[IrLength-1:1]}; + end + + // capture IR register + if (capture_ir) begin + jtag_ir_shift_d = IrLength'(4'b0101); + end + + // update IR register + if (update_ir) begin + jtag_ir_d = ir_reg_e'(jtag_ir_shift_q); + end + + // synchronous test-logic reset + if (test_logic_reset_o) begin + jtag_ir_shift_d = '0; + jtag_ir_d = IDCODE; + end + end + + always_ff @(posedge tck_i, negedge trst_ni) begin : p_jtag_ir_reg + if (!trst_ni) begin + jtag_ir_shift_q <= '0; + jtag_ir_q <= IDCODE; + end else begin + jtag_ir_shift_q <= jtag_ir_shift_d; + jtag_ir_q <= jtag_ir_d; + end + end + + // ---------------- + // TAP DR Regs + // ---------------- + // - Bypass + // - IDCODE + // - DTM CS + logic [31:0] idcode_d, idcode_q; + logic idcode_select; + logic bypass_select; + dtmcs_t dtmcs_d, dtmcs_q; + logic bypass_d, bypass_q; // this is a 1-bit register + + assign dmi_reset_o = dtmcs_q.dmireset; + + always_comb begin + idcode_d = idcode_q; + bypass_d = bypass_q; + dtmcs_d = dtmcs_q; + + if (capture_dr_o) begin + if (idcode_select) idcode_d = IdcodeValue; + if (bypass_select) bypass_d = 1'b0; + if (dtmcs_select_o) begin + dtmcs_d = '{ + zero1 : '0, + dmihardreset : 1'b0, + dmireset : 1'b0, + zero0 : '0, + idle : 3'd1, // 1: Enter Run-Test/Idle and leave it immediately + dmistat : dmi_error_i, // 0: No error, 1: Op failed, 2: too fast + abits : 6'd7, // The size of address in dmi + version : 4'd1 // Version described in spec version 0.13 (and later?) + }; + end + end + + if (shift_dr_o) begin + if (idcode_select) idcode_d = {td_i, 31'(idcode_q >> 1)}; + if (bypass_select) bypass_d = td_i; + if (dtmcs_select_o) dtmcs_d = {td_i, 31'(dtmcs_q >> 1)}; + end + + if (test_logic_reset_o) begin + idcode_d = IdcodeValue; + bypass_d = 1'b0; + end + end + + // ---------------- + // Data reg select + // ---------------- + always_comb begin : p_data_reg_sel + dmi_access_o = 1'b0; + dtmcs_select_o = 1'b0; + idcode_select = 1'b0; + bypass_select = 1'b0; + unique case (jtag_ir_q) + BYPASS0: bypass_select = 1'b1; + IDCODE: idcode_select = 1'b1; + DTMCSR: dtmcs_select_o = 1'b1; + DMIACCESS: dmi_access_o = 1'b1; + BYPASS1: bypass_select = 1'b1; + default: bypass_select = 1'b1; + endcase + end + + // ---------------- + // Output select + // ---------------- + logic tdo_mux; + + always_comb begin : p_out_sel + // we are shifting out the IR register + if (shift_ir) begin + tdo_mux = jtag_ir_shift_q[0]; + // here we are shifting the DR register + end else begin + unique case (jtag_ir_q) + IDCODE: tdo_mux = idcode_q[0]; // Reading ID code + DTMCSR: tdo_mux = dtmcs_q.version[0]; + DMIACCESS: tdo_mux = dmi_tdo_i; // Read from DMI TDO + default: tdo_mux = bypass_q; // BYPASS instruction + endcase + end + end + + // ---------------- + // DFT + // ---------------- + logic tck_n; + + prim_generic_clock_inv #( + .HasScanMode(1'b1) + ) i_tck_inv ( + .clk_i ( tck_i ), + .clk_no ( tck_n ), + .scanmode_i ( testmode_i ) + ); + + // TDO changes state at negative edge of TCK + always_ff @(posedge tck_n, negedge trst_ni) begin : p_tdo_regs + if (!trst_ni) begin + td_o <= 1'b0; + tdo_oe_o <= 1'b0; + end else begin + td_o <= tdo_mux; + tdo_oe_o <= (shift_ir | shift_dr_o); + end + end + // ---------------- + // TAP FSM + // ---------------- + // Determination of next state; purely combinatorial + always_comb begin : p_tap_fsm + + test_logic_reset_o = 1'b0; + + capture_dr_o = 1'b0; + shift_dr_o = 1'b0; + update_dr_o = 1'b0; + + capture_ir = 1'b0; + shift_ir = 1'b0; + // pause_ir = 1'b0; unused + update_ir = 1'b0; + + unique case (tap_state_q) + TestLogicReset: begin + tap_state_d = (tms_i) ? TestLogicReset : RunTestIdle; + test_logic_reset_o = 1'b1; + end + RunTestIdle: begin + tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; + end + // DR Path + SelectDrScan: begin + tap_state_d = (tms_i) ? SelectIrScan : CaptureDr; + end + CaptureDr: begin + capture_dr_o = 1'b1; + tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; + end + ShiftDr: begin + shift_dr_o = 1'b1; + tap_state_d = (tms_i) ? Exit1Dr : ShiftDr; + end + Exit1Dr: begin + tap_state_d = (tms_i) ? UpdateDr : PauseDr; + end + PauseDr: begin + tap_state_d = (tms_i) ? Exit2Dr : PauseDr; + end + Exit2Dr: begin + tap_state_d = (tms_i) ? UpdateDr : ShiftDr; + end + UpdateDr: begin + update_dr_o = 1'b1; + tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; + end + // IR Path + SelectIrScan: begin + tap_state_d = (tms_i) ? TestLogicReset : CaptureIr; + end + // In this controller state, the shift register bank in the + // Instruction Register parallel loads a pattern of fixed values on + // the rising edge of TCK. The last two significant bits must always + // be "01". + CaptureIr: begin + capture_ir = 1'b1; + tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; + end + // In this controller state, the instruction register gets connected + // between TDI and TDO, and the captured pattern gets shifted on + // each rising edge of TCK. The instruction available on the TDI + // pin is also shifted in to the instruction register. + ShiftIr: begin + shift_ir = 1'b1; + tap_state_d = (tms_i) ? Exit1Ir : ShiftIr; + end + Exit1Ir: begin + tap_state_d = (tms_i) ? UpdateIr : PauseIr; + end + PauseIr: begin + // pause_ir = 1'b1; // unused + tap_state_d = (tms_i) ? Exit2Ir : PauseIr; + end + Exit2Ir: begin + tap_state_d = (tms_i) ? UpdateIr : ShiftIr; + end + // In this controller state, the instruction in the instruction + // shift register is latched to the latch bank of the Instruction + // Register on every falling edge of TCK. This instruction becomes + // the current instruction once it is latched. + UpdateIr: begin + update_ir = 1'b1; + tap_state_d = (tms_i) ? SelectDrScan : RunTestIdle; + end + //default: ; // can't actually happen since case is full + endcase + end + + always_ff @(posedge tck_i or negedge trst_ni) begin : p_regs + if (!trst_ni) begin + tap_state_q <= RunTestIdle; + idcode_q <= IdcodeValue; + bypass_q <= 1'b0; + dtmcs_q <= '0; + end else begin + tap_state_q <= tap_state_d; + idcode_q <= idcode_d; + bypass_q <= bypass_d; + dtmcs_q <= dtmcs_d; + end + end + +endmodule : dmi_jtag_tap
diff --git a/verilog/rtl/down_clocking_even.v b/verilog/rtl/down_clocking_even.v new file mode 100644 index 0000000..49a06ca --- /dev/null +++ b/verilog/rtl/down_clocking_even.v
@@ -0,0 +1,37 @@ +/*Down clocking module +Output clock frequency is the original frequency divided by an even number +*/ +module down_clocking_even( +input clk_i, +input rst_ni, +input [15:0]i_divisor, +output o_clk +); + +wire [15:0]divisor; +wire borrow; + +minus_one minus_one_0( +.i_operand(i_divisor), +.o_result(divisor), +.o_borrow(borrow) +); + +wire go; +assign go=((i_divisor!=0)&&rst_ni); +reg [15:0]ct; +reg clk; +always@(posedge clk_i ) + if(!rst_ni)begin + ct<=0; + clk<=0; + end + else if(go)begin + if(ct>=divisor)begin + ct<=0; + clk<=~clk; + end + else ct<=ct+1; + end +assign o_clk=go?clk:clk_i; +endmodule \ No newline at end of file
diff --git a/verilog/rtl/down_clocking_odd.v b/verilog/rtl/down_clocking_odd.v new file mode 100644 index 0000000..3395908 --- /dev/null +++ b/verilog/rtl/down_clocking_odd.v
@@ -0,0 +1,69 @@ +/*Author: Zhuxu + m99a1@yahoo.cn +Down clocking module +Output clock frequency is the original frequency divided by an odd number +*/ +module down_clocking_odd( +input clk_i, +input rst_ni, +input [15:0]i_divisor, +output o_clk +); + +reg a,b; +wire c; + +assign c=(~a)&(~b); +wire [15:0]divisor; +wire borrow; +minus_one minus_one_0( +.i_operand(i_divisor), +.o_result(divisor), +.o_borrow(borrow) +); + +wire go; +assign go=((i_divisor!=0)&&rst_ni); +reg [15:0]ct_0; +always@(posedge clk_i ) + if(!rst_ni)begin + a<=0; + ct_0<=0; + end + else if(go)begin + if(a)begin + if(ct_0>=divisor)begin + ct_0<=0; + a<=0; + end + else ct_0<=ct_0+1; + end + else if(c)a<=c; + end + + +reg [15:0]ct_1; +always@(negedge clk_i ) + if(!rst_ni)begin + b<=0; + ct_1<=0; + end + else if(go)begin + if(b)begin + if(ct_1>=divisor)begin + ct_1<=0; + b<=0; + end + else ct_1<=ct_1+1; + end + else if(c)b<=c; + end + +reg clk; +always@(posedge c) + if(!rst_ni)clk<=0; + else clk<=~clk; + +assign o_clk=go?clk:clk_i; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/fifo_async.sv b/verilog/rtl/fifo_async.sv new file mode 100644 index 0000000..0add43c --- /dev/null +++ b/verilog/rtl/fifo_async.sv
@@ -0,0 +1,203 @@ + +// Generic asynchronous fifo for use in a variety of devices. + + +module fifo_async #( + parameter int unsigned Width = 16, + parameter int unsigned Depth = 3, + localparam int unsigned DepthW = $clog2(Depth+1) // derived parameter representing [0..Depth] +) ( + // write port + input clk_wr_i, + input rst_wr_ni, + input wvalid_i, + output wready_o, + input [Width-1:0] wdata_i, + output [DepthW-1:0] wdepth_o, + + // read port + input clk_rd_i, + input rst_rd_ni, + output rvalid_o, + input rready_i, + output [Width-1:0] rdata_o, + output [DepthW-1:0] rdepth_o +); + + + localparam int unsigned PTRV_W = $clog2(Depth); + localparam logic [PTRV_W-1:0] DepthMinus1 = PTRV_W'(Depth - 1); + localparam int unsigned PTR_WIDTH = PTRV_W+1; + + logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr; + logic [PTR_WIDTH-1:0] fifo_wptr_sync_combi, fifo_rptr_sync; + logic [PTR_WIDTH-1:0] fifo_wptr_gray_sync, fifo_rptr_gray_sync; + logic [PTR_WIDTH-1:0] fifo_wptr_gray, fifo_rptr_gray; + logic fifo_incr_wptr, fifo_incr_rptr, empty; + + logic full_wclk, full_rclk; + + assign wready_o = !full_wclk; + assign rvalid_o = !empty; + + // create the write and read pointers + + assign fifo_incr_wptr = wvalid_i & wready_o; + assign fifo_incr_rptr = rvalid_o & rready_i; + + /////////////////// + // write pointer // + /////////////////// + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) + if (!rst_wr_ni) begin + fifo_wptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_wptr) begin + if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + + // gray-coded version + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) + if (!rst_wr_ni) begin + fifo_wptr_gray <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_wptr) begin + if (fifo_wptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_wptr_gray <= dec2gray({~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}); + end else begin + fifo_wptr_gray <= dec2gray(fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}); + end + end + + prim_generic_flop_2sync #(.Width(PTR_WIDTH)) sync_wptr ( + .clk_i (clk_rd_i), + .rst_ni (rst_rd_ni), + .d_i (fifo_wptr_gray), + .q_o (fifo_wptr_gray_sync)); + + assign fifo_wptr_sync_combi = gray2dec(fifo_wptr_gray_sync); + + ////////////////// + // read pointer // + ////////////////// + + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) + if (!rst_rd_ni) begin + fifo_rptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_rptr) begin + if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + + // gray-coded version + always_ff @(posedge clk_rd_i or negedge rst_rd_ni) + if (!rst_rd_ni) begin + fifo_rptr_gray <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_rptr) begin + if (fifo_rptr[PTR_WIDTH-2:0] == DepthMinus1) begin + fifo_rptr_gray <= dec2gray({~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}); + end else begin + fifo_rptr_gray <= dec2gray(fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}); + end + end + + prim_generic_flop_2sync #(.Width(PTR_WIDTH)) sync_rptr ( + .clk_i (clk_wr_i), + .rst_ni (rst_wr_ni), + .d_i (fifo_rptr_gray), + .q_o (fifo_rptr_gray_sync)); + + always_ff @(posedge clk_wr_i or negedge rst_wr_ni) + if (!rst_wr_ni) begin + fifo_rptr_sync <= {PTR_WIDTH{1'b0}}; + end else begin + fifo_rptr_sync <= gray2dec(fifo_rptr_gray_sync); + end + + ////////////////// + // empty / full // + ////////////////// + + assign full_wclk = (fifo_wptr == (fifo_rptr_sync ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + assign full_rclk = (fifo_wptr_sync_combi == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + + // Current depth in the write clock side + logic wptr_msb; + logic rptr_sync_msb; + logic [PTRV_W-1:0] wptr_value; + logic [PTRV_W-1:0] rptr_sync_value; + assign wptr_msb = fifo_wptr[PTR_WIDTH-1]; + assign rptr_sync_msb = fifo_rptr_sync[PTR_WIDTH-1]; + assign wptr_value = fifo_wptr[0+:PTRV_W]; + assign rptr_sync_value = fifo_rptr_sync[0+:PTRV_W]; + assign wdepth_o = (full_wclk) ? DepthW'(Depth) : + (wptr_msb == rptr_sync_msb) ? DepthW'(wptr_value) - DepthW'(rptr_sync_value) : + (DepthW'(Depth) - DepthW'(rptr_sync_value) + DepthW'(wptr_value)) ; + + // Same again in the read clock side + assign empty = (fifo_wptr_sync_combi == fifo_rptr); + logic rptr_msb; + logic wptr_sync_msb; + logic [PTRV_W-1:0] rptr_value; + logic [PTRV_W-1:0] wptr_sync_value; + assign wptr_sync_msb = fifo_wptr_sync_combi[PTR_WIDTH-1]; + assign rptr_msb = fifo_rptr[PTR_WIDTH-1]; + assign wptr_sync_value = fifo_wptr_sync_combi[0+:PTRV_W]; + assign rptr_value = fifo_rptr[0+:PTRV_W]; + assign rdepth_o = (full_rclk) ? DepthW'(Depth) : + (wptr_sync_msb == rptr_msb) ? DepthW'(wptr_sync_value) - DepthW'(rptr_value) : + (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_sync_value)) ; + + ///////////// + // storage // + ///////////// + + logic [Width-1:0] storage [Depth]; + + always_ff @(posedge clk_wr_i) + if (fifo_incr_wptr) begin + storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata_i; + end + + assign rdata_o = storage[fifo_rptr[PTR_WIDTH-2:0]]; + + // gray code conversion functions. algorithm walks up from 0..N-1 + // then flips the upper bit and walks down from N-1 to 0. + + function automatic [PTR_WIDTH-1:0] dec2gray(input logic [PTR_WIDTH-1:0] decval); + logic [PTR_WIDTH-1:0] decval_sub; + logic [PTR_WIDTH-2:0] decval_in; + logic unused_decval_msb; + + decval_sub = (PTR_WIDTH)'(Depth) - {1'b0, decval[PTR_WIDTH-2:0]} - 1'b1; + + {unused_decval_msb, decval_in} = decval[PTR_WIDTH-1] ? decval_sub : decval; + // Was done in two assigns for low bits and top bit + // but that generates a (bogus) verilator warning, so do in one assign + dec2gray = {decval[PTR_WIDTH-1], + {1'b0,decval_in[PTR_WIDTH-2:1]} ^ decval_in[PTR_WIDTH-2:0]}; + endfunction + + function automatic [PTR_WIDTH-1:0] gray2dec(input logic [PTR_WIDTH-1:0] grayval); + logic [PTR_WIDTH-2:0] dec_tmp, dec_tmp_sub; + logic unused_decsub_msb; + + dec_tmp[PTR_WIDTH-2] = grayval[PTR_WIDTH-2]; + for (int i = PTR_WIDTH-3; i >= 0; i--) + dec_tmp[i] = dec_tmp[i+1]^grayval[i]; + {unused_decsub_msb, dec_tmp_sub} = (PTR_WIDTH-1)'(Depth) - {1'b0, dec_tmp} - 1'b1; + if (grayval[PTR_WIDTH-1]) + gray2dec = {1'b1,dec_tmp_sub}; + else + gray2dec = {1'b0,dec_tmp}; + endfunction + + + +endmodule
diff --git a/verilog/rtl/fifo_sync.sv b/verilog/rtl/fifo_sync.sv new file mode 100644 index 0000000..b0ab38a --- /dev/null +++ b/verilog/rtl/fifo_sync.sv
@@ -0,0 +1,147 @@ + +// Generic synchronous fifo for use in a variety of devices. + + +module fifo_sync #( + parameter int unsigned Width = 16, + parameter bit Pass = 1'b1, // if == 1 allow requests to pass through empty FIFO + parameter int unsigned Depth = 4, + parameter bit OutputZeroIfEmpty = 1'b1, // if == 1 always output 0 when FIFO is empty + // derived parameter + localparam int DepthW = tlul_pkg::vbits(Depth+1) +) ( + input clk_i, + input rst_ni, + // synchronous clear / flush port + input clr_i, + // write port + input wvalid_i, + output wready_o, + input [Width-1:0] wdata_i, + // read port + output rvalid_o, + input rready_i, + output [Width-1:0] rdata_o, + // occupancy + output [DepthW-1:0] depth_o +); + + // FIFO is in complete passthrough mode + if (Depth == 0) begin : gen_passthru_fifo + + assign depth_o = 1'b0; //output is meaningless + + // devie facing + assign rvalid_o = wvalid_i; + assign rdata_o = wdata_i; + + // host facing + assign wready_o = rready_i; + + // this avoids lint warnings + logic unused_clr; + assign unused_clr = clr_i; + + // Normal FIFO construction + end else begin : gen_normal_fifo + + localparam int unsigned PTRV_W = tlul_pkg::vbits(Depth); + localparam int unsigned PTR_WIDTH = PTRV_W+1; + + logic [PTR_WIDTH-1:0] fifo_wptr, fifo_rptr; + logic fifo_incr_wptr, fifo_incr_rptr, fifo_empty; + + // create the write and read pointers + logic full, empty; + logic wptr_msb; + logic rptr_msb; + logic [PTRV_W-1:0] wptr_value; + logic [PTRV_W-1:0] rptr_value; + + assign wptr_msb = fifo_wptr[PTR_WIDTH-1]; + assign rptr_msb = fifo_rptr[PTR_WIDTH-1]; + assign wptr_value = fifo_wptr[0+:PTRV_W]; + assign rptr_value = fifo_rptr[0+:PTRV_W]; + assign depth_o = (full) ? DepthW'(Depth) : + (wptr_msb == rptr_msb) ? DepthW'(wptr_value) - DepthW'(rptr_value) : + (DepthW'(Depth) - DepthW'(rptr_value) + DepthW'(wptr_value)) ; + + assign fifo_incr_wptr = wvalid_i & wready_o; + assign fifo_incr_rptr = rvalid_o & rready_i; + + assign wready_o = ~full; + assign rvalid_o = ~empty; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fifo_wptr <= {(PTR_WIDTH){1'b0}}; + end else if (clr_i) begin + fifo_wptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_wptr) begin + if (fifo_wptr[PTR_WIDTH-2:0] == (PTR_WIDTH-1)'(Depth-1)) begin + fifo_wptr <= {~fifo_wptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_wptr <= fifo_wptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + fifo_rptr <= {(PTR_WIDTH){1'b0}}; + end else if (clr_i) begin + fifo_rptr <= {(PTR_WIDTH){1'b0}}; + end else if (fifo_incr_rptr) begin + if (fifo_rptr[PTR_WIDTH-2:0] == (PTR_WIDTH-1)'(Depth-1)) begin + fifo_rptr <= {~fifo_rptr[PTR_WIDTH-1],{(PTR_WIDTH-1){1'b0}}}; + end else begin + fifo_rptr <= fifo_rptr + {{(PTR_WIDTH-1){1'b0}},1'b1}; + end + end + end + + assign full = (fifo_wptr == (fifo_rptr ^ {1'b1,{(PTR_WIDTH-1){1'b0}}})); + assign fifo_empty = (fifo_wptr == fifo_rptr); + + + // the generate blocks below are needed to avoid lint errors due to array indexing + // in the where the fifo only has one storage element + logic [Depth-1:0][Width-1:0] storage; + logic [Width-1:0] storage_rdata; + if (Depth == 1) begin : gen_depth_eq1 + assign storage_rdata = storage[0]; + + always_ff @(posedge clk_i) + if (fifo_incr_wptr) begin + storage[0] <= wdata_i; + end + // fifo with more than one storage element + end else begin : gen_depth_gt1 + assign storage_rdata = storage[fifo_rptr[PTR_WIDTH-2:0]]; + + always_ff @(posedge clk_i) + if (fifo_incr_wptr) begin + storage[fifo_wptr[PTR_WIDTH-2:0]] <= wdata_i; + end + end + + logic [Width-1:0] rdata_int; + if (Pass == 1'b1) begin : gen_pass + assign rdata_int = (fifo_empty && wvalid_i) ? wdata_i : storage_rdata; + assign empty = fifo_empty & ~wvalid_i; + end else begin : gen_nopass + assign rdata_int = storage_rdata; + assign empty = fifo_empty; + end + + if (OutputZeroIfEmpty == 1'b1) begin : gen_output_zero + assign rdata_o = empty ? 'b0 : rdata_int; + end else begin : gen_no_output_zero + assign rdata_o = rdata_int; + end + + end // block: gen_normal_fifo + + + +endmodule
diff --git a/verilog/rtl/fpnew_cast_multi.sv b/verilog/rtl/fpnew_cast_multi.sv new file mode 100644 index 0000000..98ed07b --- /dev/null +++ b/verilog/rtl/fpnew_cast_multi.sv
@@ -0,0 +1,758 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_cast_multi #( + parameter fpnew_pkg::fmt_logic_t FpFmtConfig = '1, + parameter fpnew_pkg::ifmt_logic_t IntFmtConfig = '1, + // FPU configuration + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + parameter type AuxType = logic, + // Do not change + localparam int unsigned WIDTH = fpnew_pkg::maximum(fpnew_pkg::max_fp_width(FpFmtConfig), + fpnew_pkg::max_int_width(IntFmtConfig)), + localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [WIDTH-1:0] operands_i, // 1 operand + input logic [NUM_FORMATS-1:0] is_boxed_i, // 1 operand + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input fpnew_pkg::fp_format_e src_fmt_i, + input fpnew_pkg::fp_format_e dst_fmt_i, + input fpnew_pkg::int_format_e int_fmt_i, + input TagType tag_i, + input AuxType aux_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [WIDTH-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + output AuxType aux_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + // ---------- + // Constants + // ---------- + localparam int unsigned NUM_INT_FORMATS = fpnew_pkg::NUM_INT_FORMATS; + localparam int unsigned MAX_INT_WIDTH = fpnew_pkg::max_int_width(IntFmtConfig); + + localparam fpnew_pkg::fp_encoding_t SUPER_FORMAT = fpnew_pkg::super_format(FpFmtConfig); + + localparam int unsigned SUPER_EXP_BITS = SUPER_FORMAT.exp_bits; + localparam int unsigned SUPER_MAN_BITS = SUPER_FORMAT.man_bits; + localparam int unsigned SUPER_BIAS = 2**(SUPER_EXP_BITS - 1) - 1; + + // The internal mantissa includes normal bit or an entire integer + localparam int unsigned INT_MAN_WIDTH = fpnew_pkg::maximum(SUPER_MAN_BITS + 1, MAX_INT_WIDTH); + // If needed, there will be a LZC for renormalization + localparam int unsigned LZC_RESULT_WIDTH = $clog2(INT_MAN_WIDTH); + // The internal exponent must be able to represent the smallest denormal input value as signed + // or the number of bits in an integer + localparam int unsigned INT_EXP_WIDTH = fpnew_pkg::maximum($clog2(MAX_INT_WIDTH), + fpnew_pkg::maximum(SUPER_EXP_BITS, $clog2(SUPER_BIAS + SUPER_MAN_BITS))) + 1; + // Pipelines + localparam NUM_INP_REGS = PipeConfig == fpnew_pkg::BEFORE + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 1) / 3) // Second to get distributed regs + : 0); // no regs here otherwise + localparam NUM_MID_REGS = PipeConfig == fpnew_pkg::INSIDE + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 2) / 3) // First to get distributed regs + : 0); // no regs here otherwise + localparam NUM_OUT_REGS = PipeConfig == fpnew_pkg::AFTER + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? (NumPipeRegs / 3) // Last to get distributed regs + : 0); // no regs here otherwise + + // --------------- + // Input pipeline + // --------------- + // Selected pipeline output signals as non-arrays + logic [WIDTH-1:0] operands_q; + logic [NUM_FORMATS-1:0] is_boxed_q; + logic op_mod_q; + fpnew_pkg::fp_format_e src_fmt_q; + fpnew_pkg::fp_format_e dst_fmt_q; + fpnew_pkg::int_format_e int_fmt_q; + + // Input pipeline signals, index i holds signal after i register stages + logic [0:NUM_INP_REGS][WIDTH-1:0] inp_pipe_operands_q; + logic [0:NUM_INP_REGS][NUM_FORMATS-1:0] inp_pipe_is_boxed_q; + fpnew_pkg::roundmode_e [0:NUM_INP_REGS] inp_pipe_rnd_mode_q; + fpnew_pkg::operation_e [0:NUM_INP_REGS] inp_pipe_op_q; + logic [0:NUM_INP_REGS] inp_pipe_op_mod_q; + fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_src_fmt_q; + fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_dst_fmt_q; + fpnew_pkg::int_format_e [0:NUM_INP_REGS] inp_pipe_int_fmt_q; + TagType [0:NUM_INP_REGS] inp_pipe_tag_q; + AuxType [0:NUM_INP_REGS] inp_pipe_aux_q; + logic [0:NUM_INP_REGS] inp_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_INP_REGS] inp_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign inp_pipe_operands_q[0] = operands_i; + assign inp_pipe_is_boxed_q[0] = is_boxed_i; + assign inp_pipe_rnd_mode_q[0] = rnd_mode_i; + assign inp_pipe_op_q[0] = op_i; + assign inp_pipe_op_mod_q[0] = op_mod_i; + assign inp_pipe_src_fmt_q[0] = src_fmt_i; + assign inp_pipe_dst_fmt_q[0] = dst_fmt_i; + assign inp_pipe_int_fmt_q[0] = int_fmt_i; + assign inp_pipe_tag_q[0] = tag_i; + assign inp_pipe_aux_q[0] = aux_i; + assign inp_pipe_valid_q[0] = in_valid_i; + // Input stage: Propagate pipeline ready signal to updtream circuitry + assign in_ready_o = inp_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_INP_REGS; i++) begin : gen_input_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign inp_pipe_ready[i] = inp_pipe_ready[i+1] | ~inp_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(inp_pipe_valid_q[i+1], inp_pipe_valid_q[i], inp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = inp_pipe_ready[i] & inp_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(inp_pipe_operands_q[i+1], inp_pipe_operands_q[i], reg_ena, '0) + `FFL(inp_pipe_is_boxed_q[i+1], inp_pipe_is_boxed_q[i], reg_ena, '0) + `FFL(inp_pipe_rnd_mode_q[i+1], inp_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(inp_pipe_op_q[i+1], inp_pipe_op_q[i], reg_ena, fpnew_pkg::FMADD) + `FFL(inp_pipe_op_mod_q[i+1], inp_pipe_op_mod_q[i], reg_ena, '0) + `FFL(inp_pipe_src_fmt_q[i+1], inp_pipe_src_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(inp_pipe_dst_fmt_q[i+1], inp_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(inp_pipe_int_fmt_q[i+1], inp_pipe_int_fmt_q[i], reg_ena, fpnew_pkg::int_format_e'(0)) + `FFL(inp_pipe_tag_q[i+1], inp_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(inp_pipe_aux_q[i+1], inp_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: assign selected pipe outputs to signals for later use + assign operands_q = inp_pipe_operands_q[NUM_INP_REGS]; + assign is_boxed_q = inp_pipe_is_boxed_q[NUM_INP_REGS]; + assign op_mod_q = inp_pipe_op_mod_q[NUM_INP_REGS]; + assign src_fmt_q = inp_pipe_src_fmt_q[NUM_INP_REGS]; + assign dst_fmt_q = inp_pipe_dst_fmt_q[NUM_INP_REGS]; + assign int_fmt_q = inp_pipe_int_fmt_q[NUM_INP_REGS]; + + // ----------------- + // Input processing + // ----------------- + logic src_is_int, dst_is_int; // if 0, it's a float + + assign src_is_int = (inp_pipe_op_q[NUM_INP_REGS] == fpnew_pkg::I2F); + assign dst_is_int = (inp_pipe_op_q[NUM_INP_REGS] == fpnew_pkg::F2I); + + logic [INT_MAN_WIDTH-1:0] encoded_mant; // input mantissa with implicit bit + + logic [NUM_FORMATS-1:0] fmt_sign; + logic signed [NUM_FORMATS-1:0][INT_EXP_WIDTH-1:0] fmt_exponent; + logic [NUM_FORMATS-1:0][INT_MAN_WIDTH-1:0] fmt_mantissa; + logic signed [NUM_FORMATS-1:0][INT_EXP_WIDTH-1:0] fmt_shift_compensation; // for LZC + + fpnew_pkg::fp_info_t [NUM_FORMATS-1:0] info; + + logic [NUM_INT_FORMATS-1:0][INT_MAN_WIDTH-1:0] ifmt_input_val; + logic int_sign; + logic [INT_MAN_WIDTH-1:0] int_value, int_mantissa; + + // FP Input initialization + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : fmt_init_inputs + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + if (FpFmtConfig[fmt]) begin : active_format + // Classify input + fpnew_classifier #( + .FpFormat ( fpnew_pkg::fp_format_e'(fmt) ), + .NumOperands ( 1 ) + ) i_fpnew_classifier ( + .operands_i ( operands_q[FP_WIDTH-1:0] ), + .is_boxed_i ( is_boxed_q[fmt] ), + .info_o ( info[fmt] ) + ); + + assign fmt_sign[fmt] = operands_q[FP_WIDTH-1]; + assign fmt_exponent[fmt] = signed'({1'b0, operands_q[MAN_BITS+:EXP_BITS]}); + assign fmt_mantissa[fmt] = {info[fmt].is_normal, operands_q[MAN_BITS-1:0]}; // zero pad + // Compensation for the difference in mantissa widths used for leading-zero count + assign fmt_shift_compensation[fmt] = signed'(INT_MAN_WIDTH - 1 - MAN_BITS); + end else begin : inactive_format + assign info[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + assign fmt_sign[fmt] = fpnew_pkg::DONT_CARE; // format disabled + assign fmt_exponent[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + assign fmt_mantissa[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + assign fmt_shift_compensation[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + end + end + + // Sign-extend INT input + for (genvar ifmt = 0; ifmt < int'(NUM_INT_FORMATS); ifmt++) begin : gen_sign_extend_int + // Set up some constants + localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt)); + + if (IntFmtConfig[ifmt]) begin : active_format // only active formats + always_comb begin : sign_ext_input + // sign-extend value only if it's signed + ifmt_input_val[ifmt] = '{default: operands_q[INT_WIDTH-1] & ~op_mod_q}; + ifmt_input_val[ifmt][INT_WIDTH-1:0] = operands_q[INT_WIDTH-1:0]; + end + end else begin : inactive_format + assign ifmt_input_val[ifmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + end + end + + // Construct input mantissa from integer + assign int_value = ifmt_input_val[int_fmt_q]; + assign int_sign = int_value[INT_MAN_WIDTH-1] & ~op_mod_q; // only signed ints are negative + assign int_mantissa = int_sign ? unsigned'(-int_value) : int_value; // get magnitude of negative + + // select mantissa with source format + assign encoded_mant = src_is_int ? int_mantissa : fmt_mantissa[src_fmt_q]; + + // -------------- + // Normalization + // -------------- + logic signed [INT_EXP_WIDTH-1:0] src_bias; // src format bias + logic signed [INT_EXP_WIDTH-1:0] src_exp; // src format exponent (biased) + logic signed [INT_EXP_WIDTH-1:0] src_subnormal; // src is subnormal + logic signed [INT_EXP_WIDTH-1:0] src_offset; // src offset within mantissa + + assign src_bias = signed'(fpnew_pkg::bias(src_fmt_q)); + assign src_exp = fmt_exponent[src_fmt_q]; + assign src_subnormal = signed'({1'b0, info[src_fmt_q].is_subnormal}); + assign src_offset = fmt_shift_compensation[src_fmt_q]; + + logic input_sign; // input sign + logic signed [INT_EXP_WIDTH-1:0] input_exp; // unbiased true exponent + logic [INT_MAN_WIDTH-1:0] input_mant; // normalized input mantissa + logic mant_is_zero; // for integer zeroes + + logic signed [INT_EXP_WIDTH-1:0] fp_input_exp; + logic signed [INT_EXP_WIDTH-1:0] int_input_exp; + + // Input mantissa needs to be normalized + logic [LZC_RESULT_WIDTH-1:0] renorm_shamt; // renormalization shift amount + logic [LZC_RESULT_WIDTH:0] renorm_shamt_sgn; // signed form for calculations + + // Leading-zero counter is needed for renormalization + lzc #( + .WIDTH ( INT_MAN_WIDTH ), + .MODE ( 1 ) // MODE = 1 counts leading zeroes + ) i_lzc ( + .in_i ( encoded_mant ), + .cnt_o ( renorm_shamt ), + .empty_o ( mant_is_zero ) + ); + assign renorm_shamt_sgn = signed'({1'b0, renorm_shamt}); + + // Get the sign from the proper source + assign input_sign = src_is_int ? int_sign : fmt_sign[src_fmt_q]; + // Realign input mantissa, append zeroes if destination is wider + assign input_mant = encoded_mant << renorm_shamt; + // Unbias exponent and compensate for shift + assign fp_input_exp = signed'(src_exp + src_subnormal - src_bias - + renorm_shamt_sgn + src_offset); // compensate for shift + assign int_input_exp = signed'(INT_MAN_WIDTH - 1 - renorm_shamt_sgn); + + assign input_exp = src_is_int ? int_input_exp : fp_input_exp; + + logic signed [INT_EXP_WIDTH-1:0] destination_exp; // re-biased exponent for destination + + // Rebias the exponent + assign destination_exp = input_exp + signed'(fpnew_pkg::bias(dst_fmt_q)); + + // --------------- + // Internal pipeline + // --------------- + // Pipeline output signals as non-arrays + logic input_sign_q; + logic signed [INT_EXP_WIDTH-1:0] input_exp_q; + logic [INT_MAN_WIDTH-1:0] input_mant_q; + logic signed [INT_EXP_WIDTH-1:0] destination_exp_q; + logic src_is_int_q; + logic dst_is_int_q; + fpnew_pkg::fp_info_t info_q; + logic mant_is_zero_q; + logic op_mod_q2; + fpnew_pkg::roundmode_e rnd_mode_q; + fpnew_pkg::fp_format_e src_fmt_q2; + fpnew_pkg::fp_format_e dst_fmt_q2; + fpnew_pkg::int_format_e int_fmt_q2; + // Internal pipeline signals, index i holds signal after i register stages + + + logic [0:NUM_MID_REGS] mid_pipe_input_sign_q; + logic signed [0:NUM_MID_REGS][INT_EXP_WIDTH-1:0] mid_pipe_input_exp_q; + logic [0:NUM_MID_REGS][INT_MAN_WIDTH-1:0] mid_pipe_input_mant_q; + logic signed [0:NUM_MID_REGS][INT_EXP_WIDTH-1:0] mid_pipe_dest_exp_q; + logic [0:NUM_MID_REGS] mid_pipe_src_is_int_q; + logic [0:NUM_MID_REGS] mid_pipe_dst_is_int_q; + fpnew_pkg::fp_info_t [0:NUM_MID_REGS] mid_pipe_info_q; + logic [0:NUM_MID_REGS] mid_pipe_mant_zero_q; + logic [0:NUM_MID_REGS] mid_pipe_op_mod_q; + fpnew_pkg::roundmode_e [0:NUM_MID_REGS] mid_pipe_rnd_mode_q; + fpnew_pkg::fp_format_e [0:NUM_MID_REGS] mid_pipe_src_fmt_q; + fpnew_pkg::fp_format_e [0:NUM_MID_REGS] mid_pipe_dst_fmt_q; + fpnew_pkg::int_format_e [0:NUM_MID_REGS] mid_pipe_int_fmt_q; + TagType [0:NUM_MID_REGS] mid_pipe_tag_q; + AuxType [0:NUM_MID_REGS] mid_pipe_aux_q; + logic [0:NUM_MID_REGS] mid_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_MID_REGS] mid_pipe_ready; + + // Input stage: First element of pipeline is taken from upstream logic + assign mid_pipe_input_sign_q[0] = input_sign; + assign mid_pipe_input_exp_q[0] = input_exp; + assign mid_pipe_input_mant_q[0] = input_mant; + assign mid_pipe_dest_exp_q[0] = destination_exp; + assign mid_pipe_src_is_int_q[0] = src_is_int; + assign mid_pipe_dst_is_int_q[0] = dst_is_int; + assign mid_pipe_info_q[0] = info[src_fmt_q]; + assign mid_pipe_mant_zero_q[0] = mant_is_zero; + assign mid_pipe_op_mod_q[0] = op_mod_q; + assign mid_pipe_rnd_mode_q[0] = inp_pipe_rnd_mode_q[NUM_INP_REGS]; + assign mid_pipe_src_fmt_q[0] = src_fmt_q; + assign mid_pipe_dst_fmt_q[0] = dst_fmt_q; + assign mid_pipe_int_fmt_q[0] = int_fmt_q; + assign mid_pipe_tag_q[0] = inp_pipe_tag_q[NUM_INP_REGS]; + assign mid_pipe_aux_q[0] = inp_pipe_aux_q[NUM_INP_REGS]; + assign mid_pipe_valid_q[0] = inp_pipe_valid_q[NUM_INP_REGS]; + // Input stage: Propagate pipeline ready signal to input pipe + assign inp_pipe_ready[NUM_INP_REGS] = mid_pipe_ready[0]; + + // Generate the register stages + for (genvar i = 0; i < NUM_MID_REGS; i++) begin : gen_inside_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign mid_pipe_ready[i] = mid_pipe_ready[i+1] | ~mid_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(mid_pipe_valid_q[i+1], mid_pipe_valid_q[i], mid_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = mid_pipe_ready[i] & mid_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(mid_pipe_input_sign_q[i+1], mid_pipe_input_sign_q[i], reg_ena, '0) + `FFL(mid_pipe_input_exp_q[i+1], mid_pipe_input_exp_q[i], reg_ena, '0) + `FFL(mid_pipe_input_mant_q[i+1], mid_pipe_input_mant_q[i], reg_ena, '0) + `FFL(mid_pipe_dest_exp_q[i+1], mid_pipe_dest_exp_q[i], reg_ena, '0) + `FFL(mid_pipe_src_is_int_q[i+1], mid_pipe_src_is_int_q[i], reg_ena, '0) + `FFL(mid_pipe_dst_is_int_q[i+1], mid_pipe_dst_is_int_q[i], reg_ena, '0) + `FFL(mid_pipe_info_q[i+1], mid_pipe_info_q[i], reg_ena, '0) + `FFL(mid_pipe_mant_zero_q[i+1], mid_pipe_mant_zero_q[i], reg_ena, '0) + `FFL(mid_pipe_op_mod_q[i+1], mid_pipe_op_mod_q[i], reg_ena, '0) + `FFL(mid_pipe_rnd_mode_q[i+1], mid_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(mid_pipe_src_fmt_q[i+1], mid_pipe_src_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(mid_pipe_dst_fmt_q[i+1], mid_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(mid_pipe_int_fmt_q[i+1], mid_pipe_int_fmt_q[i], reg_ena, fpnew_pkg::int_format_e'(0)) + `FFL(mid_pipe_tag_q[i+1], mid_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(mid_pipe_aux_q[i+1], mid_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: assign selected pipe outputs to signals for later use + assign input_sign_q = mid_pipe_input_sign_q[NUM_MID_REGS]; + assign input_exp_q = mid_pipe_input_exp_q[NUM_MID_REGS]; + assign input_mant_q = mid_pipe_input_mant_q[NUM_MID_REGS]; + assign destination_exp_q = mid_pipe_dest_exp_q[NUM_MID_REGS]; + assign src_is_int_q = mid_pipe_src_is_int_q[NUM_MID_REGS]; + assign dst_is_int_q = mid_pipe_dst_is_int_q[NUM_MID_REGS]; + assign info_q = mid_pipe_info_q[NUM_MID_REGS]; + assign mant_is_zero_q = mid_pipe_mant_zero_q[NUM_MID_REGS]; + assign op_mod_q2 = mid_pipe_op_mod_q[NUM_MID_REGS]; + assign rnd_mode_q = mid_pipe_rnd_mode_q[NUM_MID_REGS]; + assign src_fmt_q2 = mid_pipe_src_fmt_q[NUM_MID_REGS]; + assign dst_fmt_q2 = mid_pipe_dst_fmt_q[NUM_MID_REGS]; + assign int_fmt_q2 = mid_pipe_int_fmt_q[NUM_MID_REGS]; + + // -------- + // Casting + // -------- + logic [INT_EXP_WIDTH-1:0] final_exp; // after eventual adjustments + + logic [2*INT_MAN_WIDTH:0] preshift_mant; // mantissa before final shift + logic [2*INT_MAN_WIDTH:0] destination_mant; // mantissa from shifter, with rnd bit + logic [SUPER_MAN_BITS-1:0] final_mant; // mantissa after adjustments + logic [MAX_INT_WIDTH-1:0] final_int; // integer shifted in position + + logic [$clog2(INT_MAN_WIDTH+1)-1:0] denorm_shamt; // shift amount for denormalization + + logic [1:0] fp_round_sticky_bits, int_round_sticky_bits, round_sticky_bits; + logic of_before_round, uf_before_round; + + + // Perform adjustments to mantissa and exponent + always_comb begin : cast_value + // Default assignment + final_exp = unsigned'(destination_exp_q); // take exponent as is, only look at lower bits + preshift_mant = '0; // initialize mantissa container with zeroes + denorm_shamt = SUPER_MAN_BITS - fpnew_pkg::man_bits(dst_fmt_q2); // right of mantissa + of_before_round = 1'b0; + uf_before_round = 1'b0; + + // Place mantissa to the left of the shifter + preshift_mant = input_mant_q << (INT_MAN_WIDTH + 1); + + // Handle INT casts + if (dst_is_int_q) begin + // By default right shift mantissa to be an integer + denorm_shamt = unsigned'(MAX_INT_WIDTH - 1 - input_exp_q); + // overflow: when converting to unsigned the range is larger by one + if (input_exp_q >= signed'(fpnew_pkg::int_width(int_fmt_q2) - 1 + op_mod_q2)) begin + denorm_shamt = '0; // prevent shifting + of_before_round = 1'b1; + // underflow + end else if (input_exp_q < -1) begin + denorm_shamt = MAX_INT_WIDTH + 1; // all bits go to the sticky + uf_before_round = 1'b1; + end + // Handle FP over-/underflows + end else begin + // Overflow or infinities (for proper rounding) + if ((destination_exp_q >= signed'(2**fpnew_pkg::exp_bits(dst_fmt_q2))-1) || + (~src_is_int_q && info_q.is_inf)) begin + final_exp = unsigned'(2**fpnew_pkg::exp_bits(dst_fmt_q2)-2); // largest normal value + preshift_mant = '1; // largest normal value and RS bits set + of_before_round = 1'b1; + // Denormalize underflowing values + end else if (destination_exp_q < 1 && + destination_exp_q >= -signed'(fpnew_pkg::man_bits(dst_fmt_q2))) begin + final_exp = '0; // denormal result + denorm_shamt = unsigned'(denorm_shamt + 1 - destination_exp_q); // adjust right shifting + uf_before_round = 1'b1; + // Limit the shift to retain sticky bits + end else if (destination_exp_q < -signed'(fpnew_pkg::man_bits(dst_fmt_q2))) begin + final_exp = '0; // denormal result + denorm_shamt = unsigned'(denorm_shamt + 2 + fpnew_pkg::man_bits(dst_fmt_q2)); // to sticky + uf_before_round = 1'b1; + end + end + end + + localparam NUM_FP_STICKY = 2 * INT_MAN_WIDTH - SUPER_MAN_BITS - 1; // removed mantissa, 1. and R + localparam NUM_INT_STICKY = 2 * INT_MAN_WIDTH - MAX_INT_WIDTH; // removed int and R + + // Mantissa adjustment shift + assign destination_mant = preshift_mant >> denorm_shamt; + // Extract final mantissa and round bit, discard the normal bit (for FP) + assign {final_mant, fp_round_sticky_bits[1]} = + destination_mant[2*INT_MAN_WIDTH-1-:SUPER_MAN_BITS+1]; + assign {final_int, int_round_sticky_bits[1]} = destination_mant[2*INT_MAN_WIDTH-:MAX_INT_WIDTH+1]; + // Collapse sticky bits + assign fp_round_sticky_bits[0] = (| {destination_mant[NUM_FP_STICKY-1:0]}); + assign int_round_sticky_bits[0] = (| {destination_mant[NUM_INT_STICKY-1:0]}); + + // select RS bits for destination operation + assign round_sticky_bits = dst_is_int_q ? int_round_sticky_bits : fp_round_sticky_bits; + + // ---------------------------- + // Rounding and classification + // ---------------------------- + logic [WIDTH-1:0] pre_round_abs; // absolute value of result before rnd + logic of_after_round; // overflow + logic uf_after_round; // underflow + + logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_pre_round_abs; // per format + logic [NUM_FORMATS-1:0] fmt_of_after_round; + logic [NUM_FORMATS-1:0] fmt_uf_after_round; + + logic [NUM_INT_FORMATS-1:0][WIDTH-1:0] ifmt_pre_round_abs; // per format + + logic rounded_sign; + logic [WIDTH-1:0] rounded_abs; // absolute value of result after rounding + logic result_true_zero; + + logic [WIDTH-1:0] rounded_int_res; // after possible inversion + logic rounded_int_res_zero; // after rounding + + + // Pack exponent and mantissa into proper rounding form + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_res_assemble + // Set up some constants + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + if (FpFmtConfig[fmt]) begin : active_format + always_comb begin : assemble_result + fmt_pre_round_abs[fmt] = {final_exp[EXP_BITS-1:0], final_mant[MAN_BITS-1:0]}; // 0-extend + end + end else begin : inactive_format + assign fmt_pre_round_abs[fmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Sign-extend integer result + for (genvar ifmt = 0; ifmt < int'(NUM_INT_FORMATS); ifmt++) begin : gen_int_res_sign_ext + // Set up some constants + localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt)); + + if (IntFmtConfig[ifmt]) begin : active_format + always_comb begin : assemble_result + // sign-extend reusult + ifmt_pre_round_abs[ifmt] = '{default: final_int[INT_WIDTH-1]}; + ifmt_pre_round_abs[ifmt][INT_WIDTH-1:0] = final_int[INT_WIDTH-1:0]; + end + end else begin : inactive_format + assign ifmt_pre_round_abs[ifmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Select output with destination format and operation + assign pre_round_abs = dst_is_int_q ? ifmt_pre_round_abs[int_fmt_q2] : fmt_pre_round_abs[dst_fmt_q2]; + + fpnew_rounding #( + .AbsWidth ( WIDTH ) + ) i_fpnew_rounding ( + .abs_value_i ( pre_round_abs ), + .sign_i ( input_sign_q ), // source format + .round_sticky_bits_i ( round_sticky_bits ), + .rnd_mode_i ( rnd_mode_q ), + .effective_subtraction_i ( 1'b0 ), // no operation happened + .abs_rounded_o ( rounded_abs ), + .sign_o ( rounded_sign ), + .exact_zero_o ( result_true_zero ) + ); + + logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_result; + + // Detect overflows and inject sign + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_sign_inject + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + if (FpFmtConfig[fmt]) begin : active_format + always_comb begin : post_process + // detect of / uf + fmt_uf_after_round[fmt] = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '0; // denormal + fmt_of_after_round[fmt] = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '1; // inf exp. + + // Assemble regular result, nan box short ones. Int zeroes need to be detected` + fmt_result[fmt] = '1; + fmt_result[fmt][FP_WIDTH-1:0] = src_is_int_q & mant_is_zero_q + ? '0 + : {rounded_sign, rounded_abs[EXP_BITS+MAN_BITS-1:0]}; + end + end else begin : inactive_format + assign fmt_uf_after_round[fmt] = fpnew_pkg::DONT_CARE; + assign fmt_of_after_round[fmt] = fpnew_pkg::DONT_CARE; + assign fmt_result[fmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Classification after rounding select by destination format + assign uf_after_round = fmt_uf_after_round[dst_fmt_q2]; + assign of_after_round = fmt_of_after_round[dst_fmt_q2]; + + // Negative integer result needs to be brought into two's complement + assign rounded_int_res = rounded_sign ? unsigned'(-rounded_abs) : rounded_abs; + assign rounded_int_res_zero = (rounded_int_res == '0); + + // ------------------------- + // FP Special case handling + // ------------------------- + logic [WIDTH-1:0] fp_special_result; + fpnew_pkg::status_t fp_special_status; + logic fp_result_is_special; + + logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_special_result; + + // Special result construction + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_special_results + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + localparam logic [EXP_BITS-1:0] QNAN_EXPONENT = '1; + localparam logic [MAN_BITS-1:0] QNAN_MANTISSA = 2**(MAN_BITS-1); + + if (FpFmtConfig[fmt]) begin : active_format + always_comb begin : special_results + logic [FP_WIDTH-1:0] special_res; + special_res = info_q.is_zero + ? input_sign_q << FP_WIDTH-1 // signed zero + : {1'b0, QNAN_EXPONENT, QNAN_MANTISSA}; // qNaN + + // Initialize special result with ones (NaN-box) + fmt_special_result[fmt] = '1; + fmt_special_result[fmt][FP_WIDTH-1:0] = special_res; + end + end else begin : inactive_format + assign fmt_special_result[fmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Detect special case from source format, I2F casts don't produce a special result + assign fp_result_is_special = ~src_is_int_q & (info_q.is_zero | + info_q.is_nan | + ~info_q.is_boxed); + + // Signalling input NaNs raise invalid flag, otherwise no flags set + assign fp_special_status = '{NV: info_q.is_signalling, default: 1'b0}; + + // Assemble result according to destination format + assign fp_special_result = fmt_special_result[dst_fmt_q2]; // destination format + + // -------------------------- + // INT Special case handling + // -------------------------- + logic [WIDTH-1:0] int_special_result; + fpnew_pkg::status_t int_special_status; + logic int_result_is_special; + + logic [NUM_INT_FORMATS-1:0][WIDTH-1:0] ifmt_special_result; + + // Special result construction + for (genvar ifmt = 0; ifmt < int'(NUM_INT_FORMATS); ifmt++) begin : gen_special_results_int + // Set up some constants + localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt)); + + if (IntFmtConfig[ifmt]) begin : active_format + always_comb begin : special_results + automatic logic [INT_WIDTH-1:0] special_res; + + // Default is overflow to positive max, which is 2**INT_WIDTH-1 or 2**(INT_WIDTH-1)-1 + special_res[INT_WIDTH-2:0] = '1; // alone yields 2**(INT_WIDTH-1)-1 + special_res[INT_WIDTH-1] = op_mod_q2; // for unsigned casts yields 2**INT_WIDTH-1 + + // Negative special case (except for nans) tie to -max or 0 + if (input_sign_q && !info_q.is_nan) + special_res = ~special_res; + + // Initialize special result with sign-extension + ifmt_special_result[ifmt] = '{default: special_res[INT_WIDTH-1]}; + ifmt_special_result[ifmt][INT_WIDTH-1:0] = special_res; + end + end else begin : inactive_format + assign ifmt_special_result[ifmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Detect special case from source format (inf, nan, overflow, nan-boxing or negative unsigned) + assign int_result_is_special = info_q.is_nan | info_q.is_inf | + of_before_round | ~info_q.is_boxed | + (input_sign_q & op_mod_q2 & ~rounded_int_res_zero); + + // All integer special cases are invalid + assign int_special_status = '{NV: 1'b1, default: 1'b0}; + + // Assemble result according to destination format + assign int_special_result = ifmt_special_result[int_fmt_q2]; // destination format + + // ----------------- + // Result selection + // ----------------- + fpnew_pkg::status_t int_regular_status, fp_regular_status; + + logic [WIDTH-1:0] fp_result, int_result; + fpnew_pkg::status_t fp_status, int_status; + + assign fp_regular_status.NV = src_is_int_q & (of_before_round | of_after_round); // overflow is invalid for I2F casts + assign fp_regular_status.DZ = 1'b0; // no divisions + assign fp_regular_status.OF = ~src_is_int_q & (~info_q.is_inf & (of_before_round | of_after_round)); // inf casts no OF + assign fp_regular_status.UF = uf_after_round & fp_regular_status.NX; + assign fp_regular_status.NX = src_is_int_q ? (| fp_round_sticky_bits) // overflow is invalid in i2f + : (| fp_round_sticky_bits) | (~info_q.is_inf & (of_before_round | of_after_round)); + assign int_regular_status = '{NX: (| int_round_sticky_bits), default: 1'b0}; + + assign fp_result = fp_result_is_special ? fp_special_result : fmt_result[dst_fmt_q2]; + assign fp_status = fp_result_is_special ? fp_special_status : fp_regular_status; + assign int_result = int_result_is_special ? int_special_result : rounded_int_res; + assign int_status = int_result_is_special ? int_special_status : int_regular_status; + + // Final results for output pipeline + logic [WIDTH-1:0] result_d; + fpnew_pkg::status_t status_d; + logic extension_bit; + + // Select output depending on special case detection + assign result_d = dst_is_int_q ? int_result : fp_result; + assign status_d = dst_is_int_q ? int_status : fp_status; + + // MSB of int result decides extension, otherwise NaN box + assign extension_bit = dst_is_int_q ? int_result[WIDTH-1] : 1'b1; + + // ---------------- + // Output Pipeline + // ---------------- + // Output pipeline signals, index i holds signal after i register stages + logic [0:NUM_OUT_REGS][WIDTH-1:0] out_pipe_result_q; + fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q; + logic [0:NUM_OUT_REGS] out_pipe_ext_bit_q; + TagType [0:NUM_OUT_REGS] out_pipe_tag_q; + AuxType [0:NUM_OUT_REGS] out_pipe_aux_q; + logic [0:NUM_OUT_REGS] out_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_OUT_REGS] out_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign out_pipe_result_q[0] = result_d; + assign out_pipe_status_q[0] = status_d; + assign out_pipe_ext_bit_q[0] = extension_bit; + assign out_pipe_tag_q[0] = mid_pipe_tag_q[NUM_MID_REGS]; + assign out_pipe_aux_q[0] = mid_pipe_aux_q[NUM_MID_REGS]; + assign out_pipe_valid_q[0] = mid_pipe_valid_q[NUM_MID_REGS]; + // Input stage: Propagate pipeline ready signal to inside pipe + assign mid_pipe_ready[NUM_MID_REGS] = out_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_OUT_REGS; i++) begin : gen_output_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign out_pipe_ready[i] = out_pipe_ready[i+1] | ~out_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(out_pipe_valid_q[i+1], out_pipe_valid_q[i], out_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = out_pipe_ready[i] & out_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(out_pipe_result_q[i+1], out_pipe_result_q[i], reg_ena, '0) + `FFL(out_pipe_status_q[i+1], out_pipe_status_q[i], reg_ena, '0) + `FFL(out_pipe_ext_bit_q[i+1], out_pipe_ext_bit_q[i], reg_ena, '0) + `FFL(out_pipe_tag_q[i+1], out_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(out_pipe_aux_q[i+1], out_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: Ready travels backwards from output side, driven by downstream circuitry + assign out_pipe_ready[NUM_OUT_REGS] = out_ready_i; + // Output stage: assign module outputs + assign result_o = out_pipe_result_q[NUM_OUT_REGS]; + assign status_o = out_pipe_status_q[NUM_OUT_REGS]; + assign extension_bit_o = out_pipe_ext_bit_q[NUM_OUT_REGS]; + assign tag_o = out_pipe_tag_q[NUM_OUT_REGS]; + assign aux_o = out_pipe_aux_q[NUM_OUT_REGS]; + assign out_valid_o = out_pipe_valid_q[NUM_OUT_REGS]; + assign busy_o = (| {inp_pipe_valid_q, mid_pipe_valid_q, out_pipe_valid_q}); +endmodule
diff --git a/verilog/rtl/fpnew_classifier.sv b/verilog/rtl/fpnew_classifier.sv new file mode 100644 index 0000000..5e4fab9 --- /dev/null +++ b/verilog/rtl/fpnew_classifier.sv
@@ -0,0 +1,72 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_classifier #( + parameter fpnew_pkg::fp_format_e FpFormat = fpnew_pkg::fp_format_e'(0), + parameter int unsigned NumOperands = 1, + // Do not change + localparam int unsigned WIDTH = fpnew_pkg::fp_width(FpFormat) +) ( + input logic [NumOperands-1:0][WIDTH-1:0] operands_i, + input logic [NumOperands-1:0] is_boxed_i, + output fpnew_pkg::fp_info_t [NumOperands-1:0] info_o +); + + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(FpFormat); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(FpFormat); + + // Type definition + typedef struct packed { + logic sign; + logic [EXP_BITS-1:0] exponent; + logic [MAN_BITS-1:0] mantissa; + } fp_t; + + // Iterate through all operands + for (genvar op = 0; op < int'(NumOperands); op++) begin : gen_num_values + + fp_t value; + logic is_boxed; + logic is_normal; + logic is_inf; + logic is_nan; + logic is_signalling; + logic is_quiet; + logic is_zero; + logic is_subnormal; + + // --------------- + // Classify Input + // --------------- + always_comb begin : classify_input + value = operands_i[op]; + is_boxed = is_boxed_i[op]; + is_normal = is_boxed && (value.exponent != '0) && (value.exponent != '1); + is_zero = is_boxed && (value.exponent == '0) && (value.mantissa == '0); + is_subnormal = is_boxed && (value.exponent == '0) && !is_zero; + is_inf = is_boxed && ((value.exponent == '1) && (value.mantissa == '0)); + is_nan = !is_boxed || ((value.exponent == '1) && (value.mantissa != '0)); + is_signalling = is_boxed && is_nan && (value.mantissa[MAN_BITS-1] == 1'b0); + is_quiet = is_nan && !is_signalling; + // Assign output for current input + info_o[op].is_normal = is_normal; + info_o[op].is_subnormal = is_subnormal; + info_o[op].is_zero = is_zero; + info_o[op].is_inf = is_inf; + info_o[op].is_nan = is_nan; + info_o[op].is_signalling = is_signalling; + info_o[op].is_quiet = is_quiet; + info_o[op].is_boxed = is_boxed; + end + end +endmodule
diff --git a/verilog/rtl/fpnew_divsqrt_multi.sv b/verilog/rtl/fpnew_divsqrt_multi.sv new file mode 100644 index 0000000..937d345 --- /dev/null +++ b/verilog/rtl/fpnew_divsqrt_multi.sv
@@ -0,0 +1,339 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + + +module fpnew_divsqrt_multi #( + parameter fpnew_pkg::fmt_logic_t FpFmtConfig = '1, + // FPU configuration + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::AFTER, + parameter type TagType = logic, + parameter type AuxType = logic, + // Do not change + localparam int unsigned WIDTH = fpnew_pkg::max_fp_width(FpFmtConfig), + localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [1:0][WIDTH-1:0] operands_i, // 2 operands + input logic [NUM_FORMATS-1:0][1:0] is_boxed_i, // 2 operands + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input fpnew_pkg::fp_format_e dst_fmt_i, + input TagType tag_i, + input AuxType aux_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [WIDTH-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + output AuxType aux_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + // ---------- + // Constants + // ---------- + // Pipelines + localparam NUM_INP_REGS = (PipeConfig == fpnew_pkg::BEFORE) + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? (NumPipeRegs / 2) // Last to get distributed regs + : 0); // no regs here otherwise + localparam NUM_OUT_REGS = (PipeConfig == fpnew_pkg::AFTER || PipeConfig == fpnew_pkg::INSIDE) + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 1) / 2) // First to get distributed regs + : 0); // no regs here otherwise + + // --------------- + // Input pipeline + // --------------- + // Selected pipeline output signals as non-arrays + logic [1:0][WIDTH-1:0] operands_q; + fpnew_pkg::roundmode_e rnd_mode_q; + fpnew_pkg::operation_e op_q; + fpnew_pkg::fp_format_e dst_fmt_q; + logic in_valid_q; + + // Input pipeline signals, index i holds signal after i register stages + logic [0:NUM_INP_REGS][1:0][WIDTH-1:0] inp_pipe_operands_q; + fpnew_pkg::roundmode_e [0:NUM_INP_REGS] inp_pipe_rnd_mode_q; + fpnew_pkg::operation_e [0:NUM_INP_REGS] inp_pipe_op_q; + fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_dst_fmt_q; + TagType [0:NUM_INP_REGS] inp_pipe_tag_q; + AuxType [0:NUM_INP_REGS] inp_pipe_aux_q; + logic [0:NUM_INP_REGS] inp_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_INP_REGS] inp_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign inp_pipe_operands_q[0] = operands_i; + assign inp_pipe_rnd_mode_q[0] = rnd_mode_i; + assign inp_pipe_op_q[0] = op_i; + assign inp_pipe_dst_fmt_q[0] = dst_fmt_i; + assign inp_pipe_tag_q[0] = tag_i; + assign inp_pipe_aux_q[0] = aux_i; + assign inp_pipe_valid_q[0] = in_valid_i; + // Input stage: Propagate pipeline ready signal to updtream circuitry + assign in_ready_o = inp_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_INP_REGS; i++) begin : gen_input_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign inp_pipe_ready[i] = inp_pipe_ready[i+1] | ~inp_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(inp_pipe_valid_q[i+1], inp_pipe_valid_q[i], inp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = inp_pipe_ready[i] & inp_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(inp_pipe_operands_q[i+1], inp_pipe_operands_q[i], reg_ena, '0) + `FFL(inp_pipe_rnd_mode_q[i+1], inp_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(inp_pipe_op_q[i+1], inp_pipe_op_q[i], reg_ena, fpnew_pkg::FMADD) + `FFL(inp_pipe_dst_fmt_q[i+1], inp_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(inp_pipe_tag_q[i+1], inp_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(inp_pipe_aux_q[i+1], inp_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: assign selected pipe outputs to signals for later use + assign operands_q = inp_pipe_operands_q[NUM_INP_REGS]; + assign rnd_mode_q = inp_pipe_rnd_mode_q[NUM_INP_REGS]; + assign op_q = inp_pipe_op_q[NUM_INP_REGS]; + assign dst_fmt_q = inp_pipe_dst_fmt_q[NUM_INP_REGS]; + assign in_valid_q = inp_pipe_valid_q[NUM_INP_REGS]; + + // ----------------- + // Input processing + // ----------------- + logic [1:0] divsqrt_fmt; + logic [1:0][63:0] divsqrt_operands; // those are fixed to 64bit + logic input_is_fp8; + + // Translate fpnew formats into divsqrt formats + always_comb begin : translate_fmt + unique case (dst_fmt_q) + fpnew_pkg::FP32: divsqrt_fmt = 2'b00; + fpnew_pkg::FP64: divsqrt_fmt = 2'b01; + fpnew_pkg::FP16: divsqrt_fmt = 2'b10; + fpnew_pkg::FP16ALT: divsqrt_fmt = 2'b11; + default: divsqrt_fmt = 2'b10; // maps also FP8 to FP16 + endcase + + // Only if FP8 is enabled + input_is_fp8 = FpFmtConfig[fpnew_pkg::FP8] & (dst_fmt_q == fpnew_pkg::FP8); + + // If FP8 is supported, map it to an FP16 value + divsqrt_operands[0] = input_is_fp8 ? operands_q[0] << 8 : operands_q[0]; + divsqrt_operands[1] = input_is_fp8 ? operands_q[1] << 8 : operands_q[1]; + end + + // ------------ + // Control FSM + // ------------ + logic in_ready; // input handshake with upstream + logic div_valid, sqrt_valid; // input signalling with unit + logic unit_ready, unit_done; // status signals from unit instance + logic op_starting; // high in the cycle a new operation starts + logic out_valid, out_ready; // output handshake with downstream + logic hold_result; // whether to put result into hold register + logic data_is_held; // data in hold register is valid + logic unit_busy; // valid data in flight + // FSM states + typedef enum logic [1:0] {IDLE, BUSY, HOLD} fsm_state_e; + fsm_state_e state_q, state_d; + + // Upstream ready comes from sanitization FSM + assign inp_pipe_ready[NUM_INP_REGS] = in_ready; + + // Valids are gated by the FSM ready. Invalid input ops run a sqrt to not lose illegal instr. + assign div_valid = in_valid_q & (op_q == fpnew_pkg::DIV) & in_ready & ~flush_i; + assign sqrt_valid = in_valid_q & (op_q != fpnew_pkg::DIV) & in_ready & ~flush_i; + assign op_starting = div_valid | sqrt_valid; + + // FSM to safely apply and receive data from DIVSQRT unit + always_comb begin : flag_fsm + // Default assignments + in_ready = 1'b0; + out_valid = 1'b0; + hold_result = 1'b0; + data_is_held = 1'b0; + unit_busy = 1'b0; + state_d = state_q; + + unique case (state_q) + // Waiting for work + IDLE: begin + in_ready = 1'b1; // we're ready + if (in_valid_q && unit_ready) begin // New work arrives + state_d = BUSY; // go into processing state + end + end + // Operation in progress + BUSY: begin + unit_busy = 1'b1; // data in flight + // If the unit is done with processing + if (unit_done) begin + out_valid = 1'b1; // try to commit result downstream + // If downstream accepts our result + if (out_ready) begin + state_d = IDLE; // we anticipate going back to idling.. + if (in_valid_q && unit_ready) begin // ..unless new work comes in + in_ready = 1'b1; // we acknowledge the instruction + state_d = BUSY; // and stay busy with it + end + // Otherwise if downstream is not ready for the result + end else begin + hold_result = 1'b1; // activate the hold register + state_d = HOLD; // wait for the pipeline to take the data + end + end + end + // Waiting with valid result for downstream + HOLD: begin + unit_busy = 1'b1; // data in flight + data_is_held = 1'b1; // data in hold register is valid + out_valid = 1'b1; // try to commit result downstream + // If the result is accepted by downstream + if (out_ready) begin + state_d = IDLE; // go back to idle.. + if (in_valid_q && unit_ready) begin // ..unless new work comes in + in_ready = 1'b1; // acknowledge the new transaction + state_d = BUSY; // will be busy with the next instruction + end + end + end + // fall into idle state otherwise + default: state_d = IDLE; + endcase + + // Flushing overrides the other actions + if (flush_i) begin + unit_busy = 1'b0; // data is invalidated + out_valid = 1'b0; // cancel any valid data + state_d = IDLE; // go to default state + end + end + + // FSM status register (asynch active low rst_ni) + `FF(state_q, state_d, IDLE) + + // Hold additional information while the operation is in progress + logic result_is_fp8_q; + TagType result_tag_q; + AuxType result_aux_q; + + // Fill the registers everytime a valid operation arrives (load FF, active low asynch rst) + `FFL(result_is_fp8_q, input_is_fp8, op_starting, '0) + `FFL(result_tag_q, inp_pipe_tag_q[NUM_INP_REGS], op_starting, '0) + `FFL(result_aux_q, inp_pipe_aux_q[NUM_INP_REGS], op_starting, '0) + + // ----------------- + // DIVSQRT instance + // ----------------- + logic [63:0] unit_result; + logic [WIDTH-1:0] adjusted_result, held_result_q; + fpnew_pkg::status_t unit_status, held_status_q; + + div_sqrt_top_mvp i_divsqrt_lei ( + .Clk_CI ( clk_i ), + .Rst_RBI ( rst_ni ), + .Div_start_SI ( div_valid ), + .Sqrt_start_SI ( sqrt_valid ), + .Operand_a_DI ( divsqrt_operands[0] ), + .Operand_b_DI ( divsqrt_operands[1] ), + .RM_SI ( rnd_mode_q ), + .Precision_ctl_SI ( '0 ), + .Format_sel_SI ( divsqrt_fmt ), + .Kill_SI ( flush_i ), + .Result_DO ( unit_result ), + .Fflags_SO ( unit_status ), + .Ready_SO ( unit_ready ), + .Done_SO ( unit_done ) + ); + + // Adjust result width and fix FP8 + assign adjusted_result = result_is_fp8_q ? unit_result >> 8 : unit_result; + + // The Hold register (load, no rst_ni) + `FFLNR(held_result_q, adjusted_result, hold_result, clk_i) + `FFLNR(held_status_q, unit_status, hold_result, clk_i) + + // -------------- + // Output Select + // -------------- + logic [WIDTH-1:0] result_d; + fpnew_pkg::status_t status_d; + // Prioritize hold register data + assign result_d = data_is_held ? held_result_q : adjusted_result; + assign status_d = data_is_held ? held_status_q : unit_status; + + // ---------------- + // Output Pipeline + // ---------------- + // Output pipeline signals, index i holds signal after i register stages + logic [0:NUM_OUT_REGS][WIDTH-1:0] out_pipe_result_q; + fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q; + TagType [0:NUM_OUT_REGS] out_pipe_tag_q; + AuxType [0:NUM_OUT_REGS] out_pipe_aux_q; + logic [0:NUM_OUT_REGS] out_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_OUT_REGS] out_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign out_pipe_result_q[0] = result_d; + assign out_pipe_status_q[0] = status_d; + assign out_pipe_tag_q[0] = result_tag_q; + assign out_pipe_aux_q[0] = result_aux_q; + assign out_pipe_valid_q[0] = out_valid; + // Input stage: Propagate pipeline ready signal to inside pipe + assign out_ready = out_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_OUT_REGS; i++) begin : gen_output_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign out_pipe_ready[i] = out_pipe_ready[i+1] | ~out_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(out_pipe_valid_q[i+1], out_pipe_valid_q[i], out_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = out_pipe_ready[i] & out_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(out_pipe_result_q[i+1], out_pipe_result_q[i], reg_ena, '0) + `FFL(out_pipe_status_q[i+1], out_pipe_status_q[i], reg_ena, '0) + `FFL(out_pipe_tag_q[i+1], out_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(out_pipe_aux_q[i+1], out_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: Ready travels backwards from output side, driven by downstream circuitry + assign out_pipe_ready[NUM_OUT_REGS] = out_ready_i; + // Output stage: assign module outputs + assign result_o = out_pipe_result_q[NUM_OUT_REGS]; + assign status_o = out_pipe_status_q[NUM_OUT_REGS]; + assign extension_bit_o = 1'b1; // always NaN-Box result + assign tag_o = out_pipe_tag_q[NUM_OUT_REGS]; + assign aux_o = out_pipe_aux_q[NUM_OUT_REGS]; + assign out_valid_o = out_pipe_valid_q[NUM_OUT_REGS]; + assign busy_o = (| {inp_pipe_valid_q, unit_busy, out_pipe_valid_q}); +endmodule
diff --git a/verilog/rtl/fpnew_fma.sv b/verilog/rtl/fpnew_fma.sv new file mode 100644 index 0000000..420e793 --- /dev/null +++ b/verilog/rtl/fpnew_fma.sv
@@ -0,0 +1,671 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_fma #( + parameter fpnew_pkg::fp_format_e FpFormat = fpnew_pkg::fp_format_e'(0), + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + parameter type AuxType = logic, + + localparam int unsigned WIDTH = fpnew_pkg::fp_width(FpFormat) // do not change +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [2:0][WIDTH-1:0] operands_i, // 3 operands + input logic [2:0] is_boxed_i, // 3 operands + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input TagType tag_i, + input AuxType aux_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [WIDTH-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + output AuxType aux_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + // ---------- + // Constants + // ---------- + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(FpFormat); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(FpFormat); + localparam int unsigned BIAS = fpnew_pkg::bias(FpFormat); + // Precision bits 'p' include the implicit bit + localparam int unsigned PRECISION_BITS = MAN_BITS + 1; + // The lower 2p+3 bits of the internal FMA result will be needed for leading-zero detection + localparam int unsigned LOWER_SUM_WIDTH = 2 * PRECISION_BITS + 3; + localparam int unsigned LZC_RESULT_WIDTH = $clog2(LOWER_SUM_WIDTH); + // Internal exponent width of FMA must accomodate all meaningful exponent values in order to avoid + // datapath leakage. This is either given by the exponent bits or the width of the LZC result. + // In most reasonable FP formats the internal exponent will be wider than the LZC result. + localparam int unsigned EXP_WIDTH = unsigned'(fpnew_pkg::maximum(EXP_BITS + 2, LZC_RESULT_WIDTH)); + // Shift amount width: maximum internal mantissa size is 3p+3 bits + localparam int unsigned SHIFT_AMOUNT_WIDTH = $clog2(3 * PRECISION_BITS + 3); + // Pipelines + localparam NUM_INP_REGS = PipeConfig == fpnew_pkg::BEFORE + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 1) / 3) // Second to get distributed regs + : 0); // no regs here otherwise + localparam NUM_MID_REGS = PipeConfig == fpnew_pkg::INSIDE + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 2) / 3) // First to get distributed regs + : 0); // no regs here otherwise + localparam NUM_OUT_REGS = PipeConfig == fpnew_pkg::AFTER + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? (NumPipeRegs / 3) // Last to get distributed regs + : 0); // no regs here otherwise + + // ---------------- + // Type definition + // ---------------- + typedef struct packed { + logic sign; + logic [EXP_BITS-1:0] exponent; + logic [MAN_BITS-1:0] mantissa; + } fp_t; + + // --------------- + // Input pipeline + // --------------- + // Input pipeline signals, index i holds signal after i register stages + logic [0:NUM_INP_REGS][2:0][WIDTH-1:0] inp_pipe_operands_q; + logic [0:NUM_INP_REGS][2:0] inp_pipe_is_boxed_q; + fpnew_pkg::roundmode_e [0:NUM_INP_REGS] inp_pipe_rnd_mode_q; + fpnew_pkg::operation_e [0:NUM_INP_REGS] inp_pipe_op_q; + logic [0:NUM_INP_REGS] inp_pipe_op_mod_q; + TagType [0:NUM_INP_REGS] inp_pipe_tag_q; + AuxType [0:NUM_INP_REGS] inp_pipe_aux_q; + logic [0:NUM_INP_REGS] inp_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_INP_REGS] inp_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign inp_pipe_operands_q[0] = operands_i; + assign inp_pipe_is_boxed_q[0] = is_boxed_i; + assign inp_pipe_rnd_mode_q[0] = rnd_mode_i; + assign inp_pipe_op_q[0] = op_i; + assign inp_pipe_op_mod_q[0] = op_mod_i; + assign inp_pipe_tag_q[0] = tag_i; + assign inp_pipe_aux_q[0] = aux_i; + assign inp_pipe_valid_q[0] = in_valid_i; + // Input stage: Propagate pipeline ready signal to updtream circuitry + assign in_ready_o = inp_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_INP_REGS; i++) begin : gen_input_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign inp_pipe_ready[i] = inp_pipe_ready[i+1] | ~inp_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(inp_pipe_valid_q[i+1], inp_pipe_valid_q[i], inp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = inp_pipe_ready[i] & inp_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(inp_pipe_operands_q[i+1], inp_pipe_operands_q[i], reg_ena, '0) + `FFL(inp_pipe_is_boxed_q[i+1], inp_pipe_is_boxed_q[i], reg_ena, '0) + `FFL(inp_pipe_rnd_mode_q[i+1], inp_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(inp_pipe_op_q[i+1], inp_pipe_op_q[i], reg_ena, fpnew_pkg::FMADD) + `FFL(inp_pipe_op_mod_q[i+1], inp_pipe_op_mod_q[i], reg_ena, '0) + `FFL(inp_pipe_tag_q[i+1], inp_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(inp_pipe_aux_q[i+1], inp_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + + // ----------------- + // Input processing + // ----------------- + fpnew_pkg::fp_info_t [2:0] info_q; + + // Classify input + fpnew_classifier #( + .FpFormat ( FpFormat ), + .NumOperands ( 3 ) + ) i_class_inputs ( + .operands_i ( inp_pipe_operands_q[NUM_INP_REGS] ), + .is_boxed_i ( inp_pipe_is_boxed_q[NUM_INP_REGS] ), + .info_o ( info_q ) + ); + + fp_t operand_a, operand_b, operand_c; + fpnew_pkg::fp_info_t info_a, info_b, info_c; + + // Operation selection and operand adjustment + // | \c op_q | \c op_mod_q | Operation Adjustment + // |:--------:|:-----------:|--------------------- + // | FMADD | \c 0 | FMADD: none + // | FMADD | \c 1 | FMSUB: Invert sign of operand C + // | FNMSUB | \c 0 | FNMSUB: Invert sign of operand A + // | FNMSUB | \c 1 | FNMADD: Invert sign of operands A and C + // | ADD | \c 0 | ADD: Set operand A to +1.0 + // | ADD | \c 1 | SUB: Set operand A to +1.0, invert sign of operand C + // | MUL | \c 0 | MUL: Set operand C to +0.0 + // | *others* | \c - | *invalid* + // \note \c op_mod_q always inverts the sign of the addend. + always_comb begin : op_select + + // Default assignments - packing-order-agnostic + operand_a = inp_pipe_operands_q[NUM_INP_REGS][0]; + operand_b = inp_pipe_operands_q[NUM_INP_REGS][1]; + operand_c = inp_pipe_operands_q[NUM_INP_REGS][2]; + info_a = info_q[0]; + info_b = info_q[1]; + info_c = info_q[2]; + + // op_mod_q inverts sign of operand C + operand_c.sign = operand_c.sign ^ inp_pipe_op_mod_q[NUM_INP_REGS]; + + unique case (inp_pipe_op_q[NUM_INP_REGS]) + fpnew_pkg::FMADD: ; // do nothing + fpnew_pkg::FNMSUB: operand_a.sign = ~operand_a.sign; // invert sign of product + fpnew_pkg::ADD: begin // Set multiplicand to +1 + operand_a = '{sign: 1'b0, exponent: BIAS, mantissa: '0}; + info_a = '{is_normal: 1'b1, is_boxed: 1'b1, default: 1'b0}; //normal, boxed value. + end + fpnew_pkg::MUL: begin // Set addend to -0 (for proper rounding with RDN) + operand_c = '{sign: 1'b1, exponent: '0, mantissa: '0}; + info_c = '{is_zero: 1'b1, is_boxed: 1'b1, default: 1'b0}; //zero, boxed value. + end + default: begin // propagate don't cares + operand_a = '{default: fpnew_pkg::DONT_CARE}; + operand_b = '{default: fpnew_pkg::DONT_CARE}; + operand_c = '{default: fpnew_pkg::DONT_CARE}; + info_a = '{default: fpnew_pkg::DONT_CARE}; + info_b = '{default: fpnew_pkg::DONT_CARE}; + info_c = '{default: fpnew_pkg::DONT_CARE}; + end + endcase + end + + // --------------------- + // Input classification + // --------------------- + logic any_operand_inf; + logic any_operand_nan; + logic signalling_nan; + logic effective_subtraction; + logic tentative_sign; + + // Reduction for special case handling + assign any_operand_inf = (| {info_a.is_inf, info_b.is_inf, info_c.is_inf}); + assign any_operand_nan = (| {info_a.is_nan, info_b.is_nan, info_c.is_nan}); + assign signalling_nan = (| {info_a.is_signalling, info_b.is_signalling, info_c.is_signalling}); + // Effective subtraction in FMA occurs when product and addend signs differ + assign effective_subtraction = operand_a.sign ^ operand_b.sign ^ operand_c.sign; + // The tentative sign of the FMA shall be the sign of the product + assign tentative_sign = operand_a.sign ^ operand_b.sign; + + // ---------------------- + // Special case handling + // ---------------------- + fp_t special_result; + fpnew_pkg::status_t special_status; + logic result_is_special; + + always_comb begin : special_cases + // Default assignments + special_result = '{sign: 1'b0, exponent: '1, mantissa: 2**(MAN_BITS-1)}; // canonical qNaN + special_status = '0; + result_is_special = 1'b0; + + // Handle potentially mixed nan & infinity input => important for the case where infinity and + // zero are multiplied and added to a qnan. + // RISC-V mandates raising the NV exception in these cases: + // (inf * 0) + c or (0 * inf) + c INVALID, no matter c (even quiet NaNs) + if ((info_a.is_inf && info_b.is_zero) || (info_a.is_zero && info_b.is_inf)) begin + result_is_special = 1'b1; // bypass FMA, output is the canonical qNaN + special_status.NV = 1'b1; // invalid operation + // NaN Inputs cause canonical quiet NaN at the output and maybe invalid OP + end else if (any_operand_nan) begin + result_is_special = 1'b1; // bypass FMA, output is the canonical qNaN + special_status.NV = signalling_nan; // raise the invalid operation flag if signalling + // Special cases involving infinity + end else if (any_operand_inf) begin + result_is_special = 1'b1; // bypass FMA + // Effective addition of opposite infinities (±inf - ±inf) is invalid! + if ((info_a.is_inf || info_b.is_inf) && info_c.is_inf && effective_subtraction) + special_status.NV = 1'b1; // invalid operation + // Handle cases where output will be inf because of inf product input + else if (info_a.is_inf || info_b.is_inf) begin + // Result is infinity with the sign of the product + special_result = '{sign: operand_a.sign ^ operand_b.sign, exponent: '1, mantissa: '0}; + // Handle cases where the addend is inf + end else if (info_c.is_inf) begin + // Result is inifinity with sign of the addend (= operand_c) + special_result = '{sign: operand_c.sign, exponent: '1, mantissa: '0}; + end + end + end + + // --------------------------- + // Initial exponent data path + // --------------------------- + logic signed [EXP_WIDTH-1:0] exponent_a, exponent_b, exponent_c; + logic signed [EXP_WIDTH-1:0] exponent_addend, exponent_product, exponent_difference; + logic signed [EXP_WIDTH-1:0] tentative_exponent; + + // Zero-extend exponents into signed container - implicit width extension + assign exponent_a = signed'({1'b0, operand_a.exponent}); + assign exponent_b = signed'({1'b0, operand_b.exponent}); + assign exponent_c = signed'({1'b0, operand_c.exponent}); + + // Calculate internal exponents from encoded values. Real exponents are (ex = Ex - bias + 1 - nx) + // with Ex the encoded exponent and nx the implicit bit. Internal exponents stay biased. + assign exponent_addend = signed'(exponent_c + $signed({1'b0, ~info_c.is_normal})); // 0 as subnorm + // Biased product exponent is the sum of encoded exponents minus the bias. + assign exponent_product = (info_a.is_zero || info_b.is_zero) + ? 2 - signed'(BIAS) // in case the product is zero, set minimum exp. + : signed'(exponent_a + info_a.is_subnormal + + exponent_b + info_b.is_subnormal + - signed'(BIAS)); + // Exponent difference is the addend exponent minus the product exponent + assign exponent_difference = exponent_addend - exponent_product; + // The tentative exponent will be the larger of the product or addend exponent + assign tentative_exponent = (exponent_difference > 0) ? exponent_addend : exponent_product; + + // Shift amount for addend based on exponents (unsigned as only right shifts) + logic [SHIFT_AMOUNT_WIDTH-1:0] addend_shamt; + + always_comb begin : addend_shift_amount + // Product-anchored case, saturated shift (addend is only in the sticky bit) + if (exponent_difference <= signed'(-2 * PRECISION_BITS - 1)) + addend_shamt = 3 * PRECISION_BITS + 4; + // Addend and product will have mutual bits to add + else if (exponent_difference <= signed'(PRECISION_BITS + 2)) + addend_shamt = unsigned'(signed'(PRECISION_BITS) + 3 - exponent_difference); + // Addend-anchored case, saturated shift (product is only in the sticky bit) + else + addend_shamt = 0; + end + + // ------------------ + // Product data path + // ------------------ + logic [PRECISION_BITS-1:0] mantissa_a, mantissa_b, mantissa_c; + logic [2*PRECISION_BITS-1:0] product; // the p*p product is 2p bits wide + logic [3*PRECISION_BITS+3:0] product_shifted; // addends are 3p+4 bit wide (including G/R) + + // Add implicit bits to mantissae + assign mantissa_a = {info_a.is_normal, operand_a.mantissa}; + assign mantissa_b = {info_b.is_normal, operand_b.mantissa}; + assign mantissa_c = {info_c.is_normal, operand_c.mantissa}; + + // Mantissa multiplier (a*b) + assign product = mantissa_a * mantissa_b; + + // Product is placed into a 3p+4 bit wide vector, padded with 2 bits for round and sticky: + // | 000...000 | product | RS | + // <- p+2 -> <- 2p -> < 2> + assign product_shifted = product << 2; // constant shift + + // ----------------- + // Addend data path + // ----------------- + logic [3*PRECISION_BITS+3:0] addend_after_shift; // upper 3p+4 bits are needed to go on + logic [PRECISION_BITS-1:0] addend_sticky_bits; // up to p bit of shifted addend are sticky + logic sticky_before_add; // they are compressed into a single sticky bit + logic [3*PRECISION_BITS+3:0] addend_shifted; // addends are 3p+4 bit wide (including G/R) + logic inject_carry_in; // inject carry for subtractions if needed + + // In parallel, the addend is right-shifted according to the exponent difference. Up to p bits + // are shifted out and compressed into a sticky bit. + // BEFORE THE SHIFT: + // | mantissa_c | 000..000 | + // <- p -> <- 3p+4 -> + // AFTER THE SHIFT: + // | 000..........000 | mantissa_c | 000...............0GR | sticky bits | + // <- addend_shamt -> <- p -> <- 2p+4-addend_shamt -> <- up to p -> + assign {addend_after_shift, addend_sticky_bits} = + (mantissa_c << (3 * PRECISION_BITS + 4)) >> addend_shamt; + + assign sticky_before_add = (| addend_sticky_bits); + // assign addend_after_shift[0] = sticky_before_add; + + // In case of a subtraction, the addend is inverted + assign addend_shifted = (effective_subtraction) ? ~addend_after_shift : addend_after_shift; + assign inject_carry_in = effective_subtraction & ~sticky_before_add; + + // ------ + // Adder + // ------ + logic [3*PRECISION_BITS+4:0] sum_raw; // added one bit for the carry + logic sum_carry; // observe carry bit from sum for sign fixing + logic [3*PRECISION_BITS+3:0] sum; // discard carry as sum won't overflow + logic final_sign; + + //Mantissa adder (ab+c). In normal addition, it cannot overflow. + assign sum_raw = product_shifted + addend_shifted + inject_carry_in; + assign sum_carry = sum_raw[3*PRECISION_BITS+4]; + + // Complement negative sum (can only happen in subtraction -> overflows for positive results) + assign sum = (effective_subtraction && ~sum_carry) ? -sum_raw : sum_raw; + + // In case of a mispredicted subtraction result, do a sign flip + assign final_sign = (effective_subtraction && (sum_carry == tentative_sign)) + ? 1'b1 + : (effective_subtraction ? 1'b0 : tentative_sign); + + // --------------- + // Internal pipeline + // --------------- + // Pipeline output signals as non-arrays + logic effective_subtraction_q; + logic signed [EXP_WIDTH-1:0] exponent_product_q; + logic signed [EXP_WIDTH-1:0] exponent_difference_q; + logic signed [EXP_WIDTH-1:0] tentative_exponent_q; + logic [SHIFT_AMOUNT_WIDTH-1:0] addend_shamt_q; + logic sticky_before_add_q; + logic [3*PRECISION_BITS+3:0] sum_q; + logic final_sign_q; + fpnew_pkg::roundmode_e rnd_mode_q; + logic result_is_special_q; + fp_t special_result_q; + fpnew_pkg::status_t special_status_q; + // Internal pipeline signals, index i holds signal after i register stages + logic [0:NUM_MID_REGS] mid_pipe_eff_sub_q; + logic signed [0:NUM_MID_REGS][EXP_WIDTH-1:0] mid_pipe_exp_prod_q; + logic signed [0:NUM_MID_REGS][EXP_WIDTH-1:0] mid_pipe_exp_diff_q; + logic signed [0:NUM_MID_REGS][EXP_WIDTH-1:0] mid_pipe_tent_exp_q; + logic [0:NUM_MID_REGS][SHIFT_AMOUNT_WIDTH-1:0] mid_pipe_add_shamt_q; + logic [0:NUM_MID_REGS] mid_pipe_sticky_q; + logic [0:NUM_MID_REGS][3*PRECISION_BITS+3:0] mid_pipe_sum_q; + logic [0:NUM_MID_REGS] mid_pipe_final_sign_q; + fpnew_pkg::roundmode_e [0:NUM_MID_REGS] mid_pipe_rnd_mode_q; + logic [0:NUM_MID_REGS] mid_pipe_res_is_spec_q; + fp_t [0:NUM_MID_REGS] mid_pipe_spec_res_q; + fpnew_pkg::status_t [0:NUM_MID_REGS] mid_pipe_spec_stat_q; + TagType [0:NUM_MID_REGS] mid_pipe_tag_q; + AuxType [0:NUM_MID_REGS] mid_pipe_aux_q; + logic [0:NUM_MID_REGS] mid_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_MID_REGS] mid_pipe_ready; + + // Input stage: First element of pipeline is taken from upstream logic + assign mid_pipe_eff_sub_q[0] = effective_subtraction; + assign mid_pipe_exp_prod_q[0] = exponent_product; + assign mid_pipe_exp_diff_q[0] = exponent_difference; + assign mid_pipe_tent_exp_q[0] = tentative_exponent; + assign mid_pipe_add_shamt_q[0] = addend_shamt; + assign mid_pipe_sticky_q[0] = sticky_before_add; + assign mid_pipe_sum_q[0] = sum; + assign mid_pipe_final_sign_q[0] = final_sign; + assign mid_pipe_rnd_mode_q[0] = inp_pipe_rnd_mode_q[NUM_INP_REGS]; + assign mid_pipe_res_is_spec_q[0] = result_is_special; + assign mid_pipe_spec_res_q[0] = special_result; + assign mid_pipe_spec_stat_q[0] = special_status; + assign mid_pipe_tag_q[0] = inp_pipe_tag_q[NUM_INP_REGS]; + assign mid_pipe_aux_q[0] = inp_pipe_aux_q[NUM_INP_REGS]; + assign mid_pipe_valid_q[0] = inp_pipe_valid_q[NUM_INP_REGS]; + // Input stage: Propagate pipeline ready signal to input pipe + assign inp_pipe_ready[NUM_INP_REGS] = mid_pipe_ready[0]; + + // Generate the register stages + for (genvar i = 0; i < NUM_MID_REGS; i++) begin : gen_inside_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign mid_pipe_ready[i] = mid_pipe_ready[i+1] | ~mid_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(mid_pipe_valid_q[i+1], mid_pipe_valid_q[i], mid_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = mid_pipe_ready[i] & mid_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(mid_pipe_eff_sub_q[i+1], mid_pipe_eff_sub_q[i], reg_ena, '0) + `FFL(mid_pipe_exp_prod_q[i+1], mid_pipe_exp_prod_q[i], reg_ena, '0) + `FFL(mid_pipe_exp_diff_q[i+1], mid_pipe_exp_diff_q[i], reg_ena, '0) + `FFL(mid_pipe_tent_exp_q[i+1], mid_pipe_tent_exp_q[i], reg_ena, '0) + `FFL(mid_pipe_add_shamt_q[i+1], mid_pipe_add_shamt_q[i], reg_ena, '0) + `FFL(mid_pipe_sticky_q[i+1], mid_pipe_sticky_q[i], reg_ena, '0) + `FFL(mid_pipe_sum_q[i+1], mid_pipe_sum_q[i], reg_ena, '0) + `FFL(mid_pipe_final_sign_q[i+1], mid_pipe_final_sign_q[i], reg_ena, '0) + `FFL(mid_pipe_rnd_mode_q[i+1], mid_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(mid_pipe_res_is_spec_q[i+1], mid_pipe_res_is_spec_q[i], reg_ena, '0) + `FFL(mid_pipe_spec_res_q[i+1], mid_pipe_spec_res_q[i], reg_ena, '0) + `FFL(mid_pipe_spec_stat_q[i+1], mid_pipe_spec_stat_q[i], reg_ena, '0) + `FFL(mid_pipe_tag_q[i+1], mid_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(mid_pipe_aux_q[i+1], mid_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: assign selected pipe outputs to signals for later use + assign effective_subtraction_q = mid_pipe_eff_sub_q[NUM_MID_REGS]; + assign exponent_product_q = mid_pipe_exp_prod_q[NUM_MID_REGS]; + assign exponent_difference_q = mid_pipe_exp_diff_q[NUM_MID_REGS]; + assign tentative_exponent_q = mid_pipe_tent_exp_q[NUM_MID_REGS]; + assign addend_shamt_q = mid_pipe_add_shamt_q[NUM_MID_REGS]; + assign sticky_before_add_q = mid_pipe_sticky_q[NUM_MID_REGS]; + assign sum_q = mid_pipe_sum_q[NUM_MID_REGS]; + assign final_sign_q = mid_pipe_final_sign_q[NUM_MID_REGS]; + assign rnd_mode_q = mid_pipe_rnd_mode_q[NUM_MID_REGS]; + assign result_is_special_q = mid_pipe_res_is_spec_q[NUM_MID_REGS]; + assign special_result_q = mid_pipe_spec_res_q[NUM_MID_REGS]; + assign special_status_q = mid_pipe_spec_stat_q[NUM_MID_REGS]; + + // -------------- + // Normalization + // -------------- + logic [LOWER_SUM_WIDTH-1:0] sum_lower; // lower 2p+3 bits of sum are searched + logic [LZC_RESULT_WIDTH-1:0] leading_zero_count; // the number of leading zeroes + logic signed [LZC_RESULT_WIDTH:0] leading_zero_count_sgn; // signed leading-zero count + logic lzc_zeroes; // in case only zeroes found + + logic [SHIFT_AMOUNT_WIDTH-1:0] norm_shamt; // Normalization shift amount + logic signed [EXP_WIDTH-1:0] normalized_exponent; + + logic [3*PRECISION_BITS+4:0] sum_shifted; // result after first normalization shift + logic [PRECISION_BITS:0] final_mantissa; // final mantissa before rounding with round bit + logic [2*PRECISION_BITS+2:0] sum_sticky_bits; // remaining 2p+3 sticky bits after normalization + logic sticky_after_norm; // sticky bit after normalization + + logic signed [EXP_WIDTH-1:0] final_exponent; + + assign sum_lower = sum_q[LOWER_SUM_WIDTH-1:0]; + + // Leading zero counter for cancellations + lzc #( + .WIDTH ( LOWER_SUM_WIDTH ), + .MODE ( 1 ) // MODE = 1 counts leading zeroes + ) i_lzc ( + .in_i ( sum_lower ), + .cnt_o ( leading_zero_count ), + .empty_o ( lzc_zeroes ) + ); + + assign leading_zero_count_sgn = signed'({1'b0, leading_zero_count}); + + // Normalization shift amount based on exponents and LZC (unsigned as only left shifts) + always_comb begin : norm_shift_amount + // Product-anchored case or cancellations require LZC + if ((exponent_difference_q <= 0) || (effective_subtraction_q && (exponent_difference_q <= 2))) begin + // Normal result (biased exponent > 0 and not a zero) + if ((exponent_product_q - leading_zero_count_sgn + 1 >= 0) && !lzc_zeroes) begin + // Undo initial product shift, remove the counted zeroes + norm_shamt = PRECISION_BITS + 2 + leading_zero_count; + normalized_exponent = exponent_product_q - leading_zero_count_sgn + 1; // account for shift + // Subnormal result + end else begin + // Cap the shift distance to align mantissa with minimum exponent + norm_shamt = unsigned'(signed'(PRECISION_BITS) + 2 + exponent_product_q); + normalized_exponent = 0; // subnormals encoded as 0 + end + // Addend-anchored case + end else begin + norm_shamt = addend_shamt_q; // Undo the initial shift + normalized_exponent = tentative_exponent_q; + end + end + + // Do the large normalization shift + assign sum_shifted = sum_q << norm_shamt; + + // The addend-anchored case needs a 1-bit normalization since the leading-one can be to the left + // or right of the (non-carry) MSB of the sum. + always_comb begin : small_norm + // Default assignment, discarding carry bit + {final_mantissa, sum_sticky_bits} = sum_shifted; + final_exponent = normalized_exponent; + + // The normalized sum has overflown, align right and fix exponent + if (sum_shifted[3*PRECISION_BITS+4]) begin // check the carry bit + {final_mantissa, sum_sticky_bits} = sum_shifted >> 1; + final_exponent = normalized_exponent + 1; + // The normalized sum is normal, nothing to do + end else if (sum_shifted[3*PRECISION_BITS+3]) begin // check the sum MSB + // do nothing + // The normalized sum is still denormal, align left - unless the result is not already subnormal + end else if (normalized_exponent > 1) begin + {final_mantissa, sum_sticky_bits} = sum_shifted << 1; + final_exponent = normalized_exponent - 1; + // Otherwise we're denormal + end else begin + final_exponent = '0; + end + end + + // Update the sticky bit with the shifted-out bits + assign sticky_after_norm = (| {sum_sticky_bits}) | sticky_before_add_q; + + // ---------------------------- + // Rounding and classification + // ---------------------------- + logic pre_round_sign; + logic [EXP_BITS-1:0] pre_round_exponent; + logic [MAN_BITS-1:0] pre_round_mantissa; + logic [EXP_BITS+MAN_BITS-1:0] pre_round_abs; // absolute value of result before rounding + logic [1:0] round_sticky_bits; + + logic of_before_round, of_after_round; // overflow + logic uf_before_round, uf_after_round; // underflow + logic result_zero; + + logic rounded_sign; + logic [EXP_BITS+MAN_BITS-1:0] rounded_abs; // absolute value of result after rounding + + // Classification before round. RISC-V mandates checking underflow AFTER rounding! + assign of_before_round = final_exponent >= 2**(EXP_BITS)-1; // infinity exponent is all ones + assign uf_before_round = final_exponent == 0; // exponent for subnormals capped to 0 + + // Assemble result before rounding. In case of overflow, the largest normal value is set. + assign pre_round_sign = final_sign_q; + assign pre_round_exponent = (of_before_round) ? 2**EXP_BITS-2 : unsigned'(final_exponent[EXP_BITS-1:0]); + assign pre_round_mantissa = (of_before_round) ? '1 : final_mantissa[MAN_BITS:1]; // bit 0 is R bit + assign pre_round_abs = {pre_round_exponent, pre_round_mantissa}; + + // In case of overflow, the round and sticky bits are set for proper rounding + assign round_sticky_bits = (of_before_round) ? 2'b11 : {final_mantissa[0], sticky_after_norm}; + + // Perform the rounding + fpnew_rounding #( + .AbsWidth ( EXP_BITS + MAN_BITS ) + ) i_fpnew_rounding ( + .abs_value_i ( pre_round_abs ), + .sign_i ( pre_round_sign ), + .round_sticky_bits_i ( round_sticky_bits ), + .rnd_mode_i ( rnd_mode_q ), + .effective_subtraction_i ( effective_subtraction_q ), + .abs_rounded_o ( rounded_abs ), + .sign_o ( rounded_sign ), + .exact_zero_o ( result_zero ) + ); + + // Classification after rounding + assign uf_after_round = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '0; // exponent = 0 + assign of_after_round = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '1; // exponent all ones + + // ----------------- + // Result selection + // ----------------- + logic [WIDTH-1:0] regular_result; + fpnew_pkg::status_t regular_status; + + // Assemble regular result + assign regular_result = {rounded_sign, rounded_abs}; + assign regular_status.NV = 1'b0; // only valid cases are handled in regular path + assign regular_status.DZ = 1'b0; // no divisions + assign regular_status.OF = of_before_round | of_after_round; // rounding can introduce overflow + assign regular_status.UF = uf_after_round & regular_status.NX; // only inexact results raise UF + assign regular_status.NX = (| round_sticky_bits) | of_before_round | of_after_round; + + // Final results for output pipeline + fp_t result_d; + fpnew_pkg::status_t status_d; + + // Select output depending on special case detection + assign result_d = result_is_special_q ? special_result_q : regular_result; + assign status_d = result_is_special_q ? special_status_q : regular_status; + + // ---------------- + // Output Pipeline + // ---------------- + // Output pipeline signals, index i holds signal after i register stages + fp_t [0:NUM_OUT_REGS] out_pipe_result_q; + fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q; + TagType [0:NUM_OUT_REGS] out_pipe_tag_q; + AuxType [0:NUM_OUT_REGS] out_pipe_aux_q; + logic [0:NUM_OUT_REGS] out_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_OUT_REGS] out_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign out_pipe_result_q[0] = result_d; + assign out_pipe_status_q[0] = status_d; + assign out_pipe_tag_q[0] = mid_pipe_tag_q[NUM_MID_REGS]; + assign out_pipe_aux_q[0] = mid_pipe_aux_q[NUM_MID_REGS]; + assign out_pipe_valid_q[0] = mid_pipe_valid_q[NUM_MID_REGS]; + // Input stage: Propagate pipeline ready signal to inside pipe + assign mid_pipe_ready[NUM_MID_REGS] = out_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_OUT_REGS; i++) begin : gen_output_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign out_pipe_ready[i] = out_pipe_ready[i+1] | ~out_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(out_pipe_valid_q[i+1], out_pipe_valid_q[i], out_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = out_pipe_ready[i] & out_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(out_pipe_result_q[i+1], out_pipe_result_q[i], reg_ena, '0) + `FFL(out_pipe_status_q[i+1], out_pipe_status_q[i], reg_ena, '0) + `FFL(out_pipe_tag_q[i+1], out_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(out_pipe_aux_q[i+1], out_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: Ready travels backwards from output side, driven by downstream circuitry + assign out_pipe_ready[NUM_OUT_REGS] = out_ready_i; + // Output stage: assign module outputs + assign result_o = out_pipe_result_q[NUM_OUT_REGS]; + assign status_o = out_pipe_status_q[NUM_OUT_REGS]; + assign extension_bit_o = 1'b1; // always NaN-Box result + assign tag_o = out_pipe_tag_q[NUM_OUT_REGS]; + assign aux_o = out_pipe_aux_q[NUM_OUT_REGS]; + assign out_valid_o = out_pipe_valid_q[NUM_OUT_REGS]; + assign busy_o = (| {inp_pipe_valid_q, mid_pipe_valid_q, out_pipe_valid_q}); +endmodule
diff --git a/verilog/rtl/fpnew_fma_multi.sv b/verilog/rtl/fpnew_fma_multi.sv new file mode 100644 index 0000000..840b889 --- /dev/null +++ b/verilog/rtl/fpnew_fma_multi.sv
@@ -0,0 +1,820 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_fma_multi #( + parameter fpnew_pkg::fmt_logic_t FpFmtConfig = '1, + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + parameter type AuxType = logic, + // Do not change + localparam int unsigned WIDTH = fpnew_pkg::max_fp_width(FpFmtConfig), + localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [2:0][WIDTH-1:0] operands_i, // 3 operands + input logic [NUM_FORMATS-1:0][2:0] is_boxed_i, // 3 operands + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input fpnew_pkg::fp_format_e src_fmt_i, // format of the multiplicands + input fpnew_pkg::fp_format_e dst_fmt_i, // format of the addend and result + input TagType tag_i, + input AuxType aux_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [WIDTH-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + output AuxType aux_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + // ---------- + // Constants + // ---------- + // The super-format that can hold all formats + localparam fpnew_pkg::fp_encoding_t SUPER_FORMAT = fpnew_pkg::super_format(FpFmtConfig); + + localparam int unsigned SUPER_EXP_BITS = SUPER_FORMAT.exp_bits; + localparam int unsigned SUPER_MAN_BITS = SUPER_FORMAT.man_bits; + + // Precision bits 'p' include the implicit bit + localparam int unsigned PRECISION_BITS = SUPER_MAN_BITS + 1; + // The lower 2p+3 bits of the internal FMA result will be needed for leading-zero detection + localparam int unsigned LOWER_SUM_WIDTH = 2 * PRECISION_BITS + 3; + localparam int unsigned LZC_RESULT_WIDTH = $clog2(LOWER_SUM_WIDTH); + // Internal exponent width of FMA must accomodate all meaningful exponent values in order to avoid + // datapath leakage. This is either given by the exponent bits or the width of the LZC result. + // In most reasonable FP formats the internal exponent will be wider than the LZC result. + localparam int unsigned EXP_WIDTH = fpnew_pkg::maximum(SUPER_EXP_BITS + 2, LZC_RESULT_WIDTH); + // Shift amount width: maximum internal mantissa size is 3p+3 bits + localparam int unsigned SHIFT_AMOUNT_WIDTH = $clog2(3 * PRECISION_BITS + 3); + // Pipelines + localparam NUM_INP_REGS = PipeConfig == fpnew_pkg::BEFORE + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 1) / 3) // Second to get distributed regs + : 0); // no regs here otherwise + localparam NUM_MID_REGS = PipeConfig == fpnew_pkg::INSIDE + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 2) / 3) // First to get distributed regs + : 0); // no regs here otherwise + localparam NUM_OUT_REGS = PipeConfig == fpnew_pkg::AFTER + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? (NumPipeRegs / 3) // Last to get distributed regs + : 0); // no regs here otherwise + + // ---------------- + // Type definition + // ---------------- + typedef struct packed { + logic sign; + logic [SUPER_EXP_BITS-1:0] exponent; + logic [SUPER_MAN_BITS-1:0] mantissa; + } fp_t; + + // --------------- + // Input pipeline + // --------------- + // Selected pipeline output signals as non-arrays + logic [2:0][WIDTH-1:0] operands_q; + fpnew_pkg::fp_format_e src_fmt_q; + fpnew_pkg::fp_format_e dst_fmt_q; + + // Input pipeline signals, index i holds signal after i register stages + logic [0:NUM_INP_REGS][2:0][WIDTH-1:0] inp_pipe_operands_q; + logic [0:NUM_INP_REGS][NUM_FORMATS-1:0][2:0] inp_pipe_is_boxed_q; + fpnew_pkg::roundmode_e [0:NUM_INP_REGS] inp_pipe_rnd_mode_q; + fpnew_pkg::operation_e [0:NUM_INP_REGS] inp_pipe_op_q; + logic [0:NUM_INP_REGS] inp_pipe_op_mod_q; + fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_src_fmt_q; + fpnew_pkg::fp_format_e [0:NUM_INP_REGS] inp_pipe_dst_fmt_q; + TagType [0:NUM_INP_REGS] inp_pipe_tag_q; + AuxType [0:NUM_INP_REGS] inp_pipe_aux_q; + logic [0:NUM_INP_REGS] inp_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_INP_REGS] inp_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign inp_pipe_operands_q[0] = operands_i; + assign inp_pipe_is_boxed_q[0] = is_boxed_i; + assign inp_pipe_rnd_mode_q[0] = rnd_mode_i; + assign inp_pipe_op_q[0] = op_i; + assign inp_pipe_op_mod_q[0] = op_mod_i; + assign inp_pipe_src_fmt_q[0] = src_fmt_i; + assign inp_pipe_dst_fmt_q[0] = dst_fmt_i; + assign inp_pipe_tag_q[0] = tag_i; + assign inp_pipe_aux_q[0] = aux_i; + assign inp_pipe_valid_q[0] = in_valid_i; + // Input stage: Propagate pipeline ready signal to updtream circuitry + assign in_ready_o = inp_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_INP_REGS; i++) begin : gen_input_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign inp_pipe_ready[i] = inp_pipe_ready[i+1] | ~inp_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(inp_pipe_valid_q[i+1], inp_pipe_valid_q[i], inp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = inp_pipe_ready[i] & inp_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(inp_pipe_operands_q[i+1], inp_pipe_operands_q[i], reg_ena, '0) + `FFL(inp_pipe_is_boxed_q[i+1], inp_pipe_is_boxed_q[i], reg_ena, '0) + `FFL(inp_pipe_rnd_mode_q[i+1], inp_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(inp_pipe_op_q[i+1], inp_pipe_op_q[i], reg_ena, fpnew_pkg::FMADD) + `FFL(inp_pipe_op_mod_q[i+1], inp_pipe_op_mod_q[i], reg_ena, '0) + `FFL(inp_pipe_src_fmt_q[i+1], inp_pipe_src_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(inp_pipe_dst_fmt_q[i+1], inp_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(inp_pipe_tag_q[i+1], inp_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(inp_pipe_aux_q[i+1], inp_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: assign selected pipe outputs to signals for later use + assign operands_q = inp_pipe_operands_q[NUM_INP_REGS]; + assign src_fmt_q = inp_pipe_src_fmt_q[NUM_INP_REGS]; + assign dst_fmt_q = inp_pipe_dst_fmt_q[NUM_INP_REGS]; + + // ----------------- + // Input processing + // ----------------- + logic [NUM_FORMATS-1:0][2:0] fmt_sign; + logic signed [NUM_FORMATS-1:0][2:0][SUPER_EXP_BITS-1:0] fmt_exponent; + logic [NUM_FORMATS-1:0][2:0][SUPER_MAN_BITS-1:0] fmt_mantissa; + + fpnew_pkg::fp_info_t [NUM_FORMATS-1:0][2:0] info_q; + + // FP Input initialization + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : fmt_init_inputs + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + if (FpFmtConfig[fmt]) begin : active_format + logic [2:0][FP_WIDTH-1:0] trimmed_ops; + + // Classify input + fpnew_classifier #( + .FpFormat ( fpnew_pkg::fp_format_e'(fmt) ), + .NumOperands ( 3 ) + ) i_fpnew_classifier ( + .operands_i ( trimmed_ops ), + .is_boxed_i ( inp_pipe_is_boxed_q[NUM_INP_REGS][fmt] ), + .info_o ( info_q[fmt] ) + ); + for (genvar op = 0; op < 3; op++) begin : gen_operands + assign trimmed_ops[op] = operands_q[op][FP_WIDTH-1:0]; + assign fmt_sign[fmt][op] = operands_q[op][FP_WIDTH-1]; + assign fmt_exponent[fmt][op] = signed'({1'b0, operands_q[op][MAN_BITS+:EXP_BITS]}); + assign fmt_mantissa[fmt][op] = {info_q[fmt][op].is_normal, operands_q[op][MAN_BITS-1:0]} << + (SUPER_MAN_BITS - MAN_BITS); // move to left of mantissa + end + end else begin : inactive_format + assign info_q[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + assign fmt_sign[fmt] = fpnew_pkg::DONT_CARE; // format disabled + assign fmt_exponent[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + assign fmt_mantissa[fmt] = '{default: fpnew_pkg::DONT_CARE}; // format disabled + end + end + + fp_t operand_a, operand_b, operand_c; + fpnew_pkg::fp_info_t info_a, info_b, info_c; + + // Operation selection and operand adjustment + // | \c op_q | \c op_mod_q | Operation Adjustment + // |:--------:|:-----------:|--------------------- + // | FMADD | \c 0 | FMADD: none + // | FMADD | \c 1 | FMSUB: Invert sign of operand C + // | FNMSUB | \c 0 | FNMSUB: Invert sign of operand A + // | FNMSUB | \c 1 | FNMADD: Invert sign of operands A and C + // | ADD | \c 0 | ADD: Set operand A to +1.0 + // | ADD | \c 1 | SUB: Set operand A to +1.0, invert sign of operand C + // | MUL | \c 0 | MUL: Set operand C to +0.0 + // | *others* | \c - | *invalid* + // \note \c op_mod_q always inverts the sign of the addend. + always_comb begin : op_select + + // Default assignments - packing-order-agnostic + operand_a = {fmt_sign[src_fmt_q][0], fmt_exponent[src_fmt_q][0], fmt_mantissa[src_fmt_q][0]}; + operand_b = {fmt_sign[src_fmt_q][1], fmt_exponent[src_fmt_q][1], fmt_mantissa[src_fmt_q][1]}; + operand_c = {fmt_sign[dst_fmt_q][2], fmt_exponent[dst_fmt_q][2], fmt_mantissa[dst_fmt_q][2]}; + info_a = info_q[src_fmt_q][0]; + info_b = info_q[src_fmt_q][1]; + info_c = info_q[dst_fmt_q][2]; + + // op_mod_q inverts sign of operand C + operand_c.sign = operand_c.sign ^ inp_pipe_op_mod_q[NUM_INP_REGS]; + + unique case (inp_pipe_op_q[NUM_INP_REGS]) + fpnew_pkg::FMADD: ; // do nothing + fpnew_pkg::FNMSUB: operand_a.sign = ~operand_a.sign; // invert sign of product + fpnew_pkg::ADD: begin // Set multiplicand to +1 + operand_a = '{sign: 1'b0, exponent: fpnew_pkg::bias(src_fmt_q), mantissa: '0}; + info_a = '{is_normal: 1'b1, is_boxed: 1'b1, default: 1'b0}; //normal, boxed value. + end + fpnew_pkg::MUL: begin // Set addend to -0 (for proper rounding with RDN) + operand_c = '{sign: 1'b1, exponent: '0, mantissa: '0}; + info_c = '{is_zero: 1'b1, is_boxed: 1'b1, default: 1'b0}; //zero, boxed value. + end + default: begin // propagate don't cares + operand_a = '{default: fpnew_pkg::DONT_CARE}; + operand_b = '{default: fpnew_pkg::DONT_CARE}; + operand_c = '{default: fpnew_pkg::DONT_CARE}; + info_a = '{default: fpnew_pkg::DONT_CARE}; + info_b = '{default: fpnew_pkg::DONT_CARE}; + info_c = '{default: fpnew_pkg::DONT_CARE}; + end + endcase + end + + // --------------------- + // Input classification + // --------------------- + logic any_operand_inf; + logic any_operand_nan; + logic signalling_nan; + logic effective_subtraction; + logic tentative_sign; + + // Reduction for special case handling + assign any_operand_inf = (| {info_a.is_inf, info_b.is_inf, info_c.is_inf}); + assign any_operand_nan = (| {info_a.is_nan, info_b.is_nan, info_c.is_nan}); + assign signalling_nan = (| {info_a.is_signalling, info_b.is_signalling, info_c.is_signalling}); + // Effective subtraction in FMA occurs when product and addend signs differ + assign effective_subtraction = operand_a.sign ^ operand_b.sign ^ operand_c.sign; + // The tentative sign of the FMA shall be the sign of the product + assign tentative_sign = operand_a.sign ^ operand_b.sign; + + // ---------------------- + // Special case handling + // ---------------------- + logic [WIDTH-1:0] special_result; + fpnew_pkg::status_t special_status; + logic result_is_special; + + logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_special_result; + fpnew_pkg::status_t [NUM_FORMATS-1:0] fmt_special_status; + logic [NUM_FORMATS-1:0] fmt_result_is_special; + + + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_special_results + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + localparam logic [EXP_BITS-1:0] QNAN_EXPONENT = '1; + localparam logic [MAN_BITS-1:0] QNAN_MANTISSA = 2**(MAN_BITS-1); + localparam logic [MAN_BITS-1:0] ZERO_MANTISSA = '0; + + if (FpFmtConfig[fmt]) begin : active_format + always_comb begin : special_results + logic [FP_WIDTH-1:0] special_res; + + // Default assignment + special_res = {1'b0, QNAN_EXPONENT, QNAN_MANTISSA}; // qNaN + fmt_special_status[fmt] = '0; + fmt_result_is_special[fmt] = 1'b0; + + // Handle potentially mixed nan & infinity input => important for the case where infinity and + // zero are multiplied and added to a qnan. + // RISC-V mandates raising the NV exception in these cases: + // (inf * 0) + c or (0 * inf) + c INVALID, no matter c (even quiet NaNs) + if ((info_a.is_inf && info_b.is_zero) || (info_a.is_zero && info_b.is_inf)) begin + fmt_result_is_special[fmt] = 1'b1; // bypass FMA, output is the canonical qNaN + fmt_special_status[fmt].NV = 1'b1; // invalid operation + // NaN Inputs cause canonical quiet NaN at the output and maybe invalid OP + end else if (any_operand_nan) begin + fmt_result_is_special[fmt] = 1'b1; // bypass FMA, output is the canonical qNaN + fmt_special_status[fmt].NV = signalling_nan; // raise the invalid operation flag if signalling + // Special cases involving infinity + end else if (any_operand_inf) begin + fmt_result_is_special[fmt] = 1'b1; // bypass FMA + // Effective addition of opposite infinities (±inf - ±inf) is invalid! + if ((info_a.is_inf || info_b.is_inf) && info_c.is_inf && effective_subtraction) + fmt_special_status[fmt].NV = 1'b1; // invalid operation + // Handle cases where output will be inf because of inf product input + else if (info_a.is_inf || info_b.is_inf) begin + // Result is infinity with the sign of the product + special_res = {operand_a.sign ^ operand_b.sign, QNAN_EXPONENT, ZERO_MANTISSA}; + // Handle cases where the addend is inf + end else if (info_c.is_inf) begin + // Result is inifinity with sign of the addend (= operand_c) + special_res = {operand_c.sign, QNAN_EXPONENT, ZERO_MANTISSA}; + end + end + // Initialize special result with ones (NaN-box) + fmt_special_result[fmt] = '1; + fmt_special_result[fmt][FP_WIDTH-1:0] = special_res; + end + end else begin : inactive_format + assign fmt_special_result[fmt] = '{default: fpnew_pkg::DONT_CARE}; + assign fmt_special_status[fmt] = '0; + assign fmt_result_is_special[fmt] = 1'b0; + end + end + + // Detect special case from source format, I2F casts don't produce a special result + assign result_is_special = fmt_result_is_special[dst_fmt_q]; // they're all the same + // Signalling input NaNs raise invalid flag, otherwise no flags set + assign special_status = fmt_special_status[dst_fmt_q]; + // Assemble result according to destination format + assign special_result = fmt_special_result[dst_fmt_q]; // destination format + + // --------------------------- + // Initial exponent data path + // --------------------------- + logic signed [EXP_WIDTH-1:0] exponent_a, exponent_b, exponent_c; + logic signed [EXP_WIDTH-1:0] exponent_addend, exponent_product, exponent_difference; + logic signed [EXP_WIDTH-1:0] tentative_exponent; + + // Zero-extend exponents into signed container - implicit width extension + assign exponent_a = signed'({1'b0, operand_a.exponent}); + assign exponent_b = signed'({1'b0, operand_b.exponent}); + assign exponent_c = signed'({1'b0, operand_c.exponent}); + + // Calculate internal exponents from encoded values. Real exponents are (ex = Ex - bias + 1 - nx) + // with Ex the encoded exponent and nx the implicit bit. Internal exponents are biased to dst fmt. + assign exponent_addend = signed'(exponent_c + $signed({1'b0, ~info_c.is_normal})); // 0 as subnorm + // Biased product exponent is the sum of encoded exponents minus the bias. + assign exponent_product = (info_a.is_zero || info_b.is_zero) // in case the product is zero, set minimum exp. + ? 2 - signed'(fpnew_pkg::bias(dst_fmt_q)) + : signed'(exponent_a + info_a.is_subnormal + + exponent_b + info_b.is_subnormal + - 2*signed'(fpnew_pkg::bias(src_fmt_q)) + + signed'(fpnew_pkg::bias(dst_fmt_q))); // rebias for dst fmt + // Exponent difference is the addend exponent minus the product exponent + assign exponent_difference = exponent_addend - exponent_product; + // The tentative exponent will be the larger of the product or addend exponent + assign tentative_exponent = (exponent_difference > 0) ? exponent_addend : exponent_product; + + // Shift amount for addend based on exponents (unsigned as only right shifts) + logic [SHIFT_AMOUNT_WIDTH-1:0] addend_shamt; + + always_comb begin : addend_shift_amount + // Product-anchored case, saturated shift (addend is only in the sticky bit) + if (exponent_difference <= signed'(-2 * PRECISION_BITS - 1)) + addend_shamt = 3 * PRECISION_BITS + 4; + // Addend and product will have mutual bits to add + else if (exponent_difference <= signed'(PRECISION_BITS + 2)) + addend_shamt = unsigned'(signed'(PRECISION_BITS) + 3 - exponent_difference); + // Addend-anchored case, saturated shift (product is only in the sticky bit) + else + addend_shamt = 0; + end + + // ------------------ + // Product data path + // ------------------ + logic [PRECISION_BITS-1:0] mantissa_a, mantissa_b, mantissa_c; + logic [2*PRECISION_BITS-1:0] product; // the p*p product is 2p bits wide + logic [3*PRECISION_BITS+3:0] product_shifted; // addends are 3p+4 bit wide (including G/R) + + // Add implicit bits to mantissae + assign mantissa_a = {info_a.is_normal, operand_a.mantissa}; + assign mantissa_b = {info_b.is_normal, operand_b.mantissa}; + assign mantissa_c = {info_c.is_normal, operand_c.mantissa}; + + // Mantissa multiplier (a*b) + assign product = mantissa_a * mantissa_b; + + // Product is placed into a 3p+4 bit wide vector, padded with 2 bits for round and sticky: + // | 000...000 | product | RS | + // <- p+2 -> <- 2p -> < 2> + assign product_shifted = product << 2; // constant shift + + // ----------------- + // Addend data path + // ----------------- + logic [3*PRECISION_BITS+3:0] addend_after_shift; // upper 3p+4 bits are needed to go on + logic [PRECISION_BITS-1:0] addend_sticky_bits; // up to p bit of shifted addend are sticky + logic sticky_before_add; // they are compressed into a single sticky bit + logic [3*PRECISION_BITS+3:0] addend_shifted; // addends are 3p+4 bit wide (including G/R) + logic inject_carry_in; // inject carry for subtractions if needed + + // In parallel, the addend is right-shifted according to the exponent difference. Up to p bits are + // shifted out and compressed into a sticky bit. + // BEFORE THE SHIFT: + // | mantissa_c | 000..000 | + // <- p -> <- 3p+4 -> + // AFTER THE SHIFT: + // | 000..........000 | mantissa_c | 000...............0GR | sticky bits | + // <- addend_shamt -> <- p -> <- 2p+4-addend_shamt -> <- up to p -> + assign {addend_after_shift, addend_sticky_bits} = + (mantissa_c << (3 * PRECISION_BITS + 4)) >> addend_shamt; + + assign sticky_before_add = (| addend_sticky_bits); + + // In case of a subtraction, the addend is inverted + assign addend_shifted = (effective_subtraction) ? ~addend_after_shift : addend_after_shift; + assign inject_carry_in = effective_subtraction & ~sticky_before_add; + + // ------ + // Adder + // ------ + logic [3*PRECISION_BITS+4:0] sum_raw; // added one bit for the carry + logic sum_carry; // observe carry bit from sum for sign fixing + logic [3*PRECISION_BITS+3:0] sum; // discard carry as sum won't overflow + logic final_sign; + + //Mantissa adder (ab+c). In normal addition, it cannot overflow. + assign sum_raw = product_shifted + addend_shifted + inject_carry_in; + assign sum_carry = sum_raw[3*PRECISION_BITS+4]; + + // Complement negative sum (can only happen in subtraction -> overflows for positive results) + assign sum = (effective_subtraction && ~sum_carry) ? -sum_raw : sum_raw; + + // In case of a mispredicted subtraction result, do a sign flip + assign final_sign = (effective_subtraction && (sum_carry == tentative_sign)) + ? 1'b1 + : (effective_subtraction ? 1'b0 : tentative_sign); + + // --------------- + // Internal pipeline + // --------------- + // Pipeline output signals as non-arrays + logic effective_subtraction_q; + logic signed [EXP_WIDTH-1:0] exponent_product_q; + logic signed [EXP_WIDTH-1:0] exponent_difference_q; + logic signed [EXP_WIDTH-1:0] tentative_exponent_q; + logic [SHIFT_AMOUNT_WIDTH-1:0] addend_shamt_q; + logic sticky_before_add_q; + logic [3*PRECISION_BITS+3:0] sum_q; + logic final_sign_q; + fpnew_pkg::fp_format_e dst_fmt_q2; + fpnew_pkg::roundmode_e rnd_mode_q; + logic result_is_special_q; + fp_t special_result_q; + fpnew_pkg::status_t special_status_q; + // Internal pipeline signals, index i holds signal after i register stages + logic [0:NUM_MID_REGS] mid_pipe_eff_sub_q; + logic signed [0:NUM_MID_REGS][EXP_WIDTH-1:0] mid_pipe_exp_prod_q; + logic signed [0:NUM_MID_REGS][EXP_WIDTH-1:0] mid_pipe_exp_diff_q; + logic signed [0:NUM_MID_REGS][EXP_WIDTH-1:0] mid_pipe_tent_exp_q; + logic [0:NUM_MID_REGS][SHIFT_AMOUNT_WIDTH-1:0] mid_pipe_add_shamt_q; + logic [0:NUM_MID_REGS] mid_pipe_sticky_q; + logic [0:NUM_MID_REGS][3*PRECISION_BITS+3:0] mid_pipe_sum_q; + logic [0:NUM_MID_REGS] mid_pipe_final_sign_q; + fpnew_pkg::roundmode_e [0:NUM_MID_REGS] mid_pipe_rnd_mode_q; + fpnew_pkg::fp_format_e [0:NUM_MID_REGS] mid_pipe_dst_fmt_q; + logic [0:NUM_MID_REGS] mid_pipe_res_is_spec_q; + fp_t [0:NUM_MID_REGS] mid_pipe_spec_res_q; + fpnew_pkg::status_t [0:NUM_MID_REGS] mid_pipe_spec_stat_q; + TagType [0:NUM_MID_REGS] mid_pipe_tag_q; + AuxType [0:NUM_MID_REGS] mid_pipe_aux_q; + logic [0:NUM_MID_REGS] mid_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_MID_REGS] mid_pipe_ready; + + // Input stage: First element of pipeline is taken from upstream logic + assign mid_pipe_eff_sub_q[0] = effective_subtraction; + assign mid_pipe_exp_prod_q[0] = exponent_product; + assign mid_pipe_exp_diff_q[0] = exponent_difference; + assign mid_pipe_tent_exp_q[0] = tentative_exponent; + assign mid_pipe_add_shamt_q[0] = addend_shamt; + assign mid_pipe_sticky_q[0] = sticky_before_add; + assign mid_pipe_sum_q[0] = sum; + assign mid_pipe_final_sign_q[0] = final_sign; + assign mid_pipe_rnd_mode_q[0] = inp_pipe_rnd_mode_q[NUM_INP_REGS]; + assign mid_pipe_dst_fmt_q[0] = dst_fmt_q; + assign mid_pipe_res_is_spec_q[0] = result_is_special; + assign mid_pipe_spec_res_q[0] = special_result; + assign mid_pipe_spec_stat_q[0] = special_status; + assign mid_pipe_tag_q[0] = inp_pipe_tag_q[NUM_INP_REGS]; + assign mid_pipe_aux_q[0] = inp_pipe_aux_q[NUM_INP_REGS]; + assign mid_pipe_valid_q[0] = inp_pipe_valid_q[NUM_INP_REGS]; + // Input stage: Propagate pipeline ready signal to input pipe + assign inp_pipe_ready[NUM_INP_REGS] = mid_pipe_ready[0]; + + // Generate the register stages + for (genvar i = 0; i < NUM_MID_REGS; i++) begin : gen_inside_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign mid_pipe_ready[i] = mid_pipe_ready[i+1] | ~mid_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(mid_pipe_valid_q[i+1], mid_pipe_valid_q[i], mid_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = mid_pipe_ready[i] & mid_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(mid_pipe_eff_sub_q[i+1], mid_pipe_eff_sub_q[i], reg_ena, '0) + `FFL(mid_pipe_exp_prod_q[i+1], mid_pipe_exp_prod_q[i], reg_ena, '0) + `FFL(mid_pipe_exp_diff_q[i+1], mid_pipe_exp_diff_q[i], reg_ena, '0) + `FFL(mid_pipe_tent_exp_q[i+1], mid_pipe_tent_exp_q[i], reg_ena, '0) + `FFL(mid_pipe_add_shamt_q[i+1], mid_pipe_add_shamt_q[i], reg_ena, '0) + `FFL(mid_pipe_sticky_q[i+1], mid_pipe_sticky_q[i], reg_ena, '0) + `FFL(mid_pipe_sum_q[i+1], mid_pipe_sum_q[i], reg_ena, '0) + `FFL(mid_pipe_final_sign_q[i+1], mid_pipe_final_sign_q[i], reg_ena, '0) + `FFL(mid_pipe_rnd_mode_q[i+1], mid_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(mid_pipe_dst_fmt_q[i+1], mid_pipe_dst_fmt_q[i], reg_ena, fpnew_pkg::fp_format_e'(0)) + `FFL(mid_pipe_res_is_spec_q[i+1], mid_pipe_res_is_spec_q[i], reg_ena, '0) + `FFL(mid_pipe_spec_res_q[i+1], mid_pipe_spec_res_q[i], reg_ena, '0) + `FFL(mid_pipe_spec_stat_q[i+1], mid_pipe_spec_stat_q[i], reg_ena, '0) + `FFL(mid_pipe_tag_q[i+1], mid_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(mid_pipe_aux_q[i+1], mid_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: assign selected pipe outputs to signals for later use + assign effective_subtraction_q = mid_pipe_eff_sub_q[NUM_MID_REGS]; + assign exponent_product_q = mid_pipe_exp_prod_q[NUM_MID_REGS]; + assign exponent_difference_q = mid_pipe_exp_diff_q[NUM_MID_REGS]; + assign tentative_exponent_q = mid_pipe_tent_exp_q[NUM_MID_REGS]; + assign addend_shamt_q = mid_pipe_add_shamt_q[NUM_MID_REGS]; + assign sticky_before_add_q = mid_pipe_sticky_q[NUM_MID_REGS]; + assign sum_q = mid_pipe_sum_q[NUM_MID_REGS]; + assign final_sign_q = mid_pipe_final_sign_q[NUM_MID_REGS]; + assign rnd_mode_q = mid_pipe_rnd_mode_q[NUM_MID_REGS]; + assign dst_fmt_q2 = mid_pipe_dst_fmt_q[NUM_MID_REGS]; + assign result_is_special_q = mid_pipe_res_is_spec_q[NUM_MID_REGS]; + assign special_result_q = mid_pipe_spec_res_q[NUM_MID_REGS]; + assign special_status_q = mid_pipe_spec_stat_q[NUM_MID_REGS]; + + // -------------- + // Normalization + // -------------- + logic [LOWER_SUM_WIDTH-1:0] sum_lower; // lower 2p+3 bits of sum are searched + logic [LZC_RESULT_WIDTH-1:0] leading_zero_count; // the number of leading zeroes + logic signed [LZC_RESULT_WIDTH:0] leading_zero_count_sgn; // signed leading-zero count + logic lzc_zeroes; // in case only zeroes found + + logic [SHIFT_AMOUNT_WIDTH-1:0] norm_shamt; // Normalization shift amount + logic signed [EXP_WIDTH-1:0] normalized_exponent; + + logic [3*PRECISION_BITS+4:0] sum_shifted; // result after first normalization shift + logic [PRECISION_BITS:0] final_mantissa; // final mantissa before rounding with round bit + logic [2*PRECISION_BITS+2:0] sum_sticky_bits; // remaining 2p+3 sticky bits after normalization + logic sticky_after_norm; // sticky bit after normalization + + logic signed [EXP_WIDTH-1:0] final_exponent; + + assign sum_lower = sum_q[LOWER_SUM_WIDTH-1:0]; + + // Leading zero counter for cancellations + lzc #( + .WIDTH ( LOWER_SUM_WIDTH ), + .MODE ( 1 ) // MODE = 1 counts leading zeroes + ) i_lzc ( + .in_i ( sum_lower ), + .cnt_o ( leading_zero_count ), + .empty_o ( lzc_zeroes ) + ); + + assign leading_zero_count_sgn = signed'({1'b0, leading_zero_count}); + + // Normalization shift amount based on exponents and LZC (unsigned as only left shifts) + always_comb begin : norm_shift_amount + // Product-anchored case or cancellations require LZC + if ((exponent_difference_q <= 0) || (effective_subtraction_q && (exponent_difference_q <= 2))) begin + // Normal result (biased exponent > 0 and not a zero) + if ((exponent_product_q - leading_zero_count_sgn + 1 >= 0) && !lzc_zeroes) begin + // Undo initial product shift, remove the counted zeroes + norm_shamt = PRECISION_BITS + 2 + leading_zero_count; + normalized_exponent = exponent_product_q - leading_zero_count_sgn + 1; // account for shift + // Subnormal result + end else begin + // Cap the shift distance to align mantissa with minimum exponent + norm_shamt = unsigned'(signed'(PRECISION_BITS + 2 + exponent_product_q)); + normalized_exponent = 0; // subnormals encoded as 0 + end + // Addend-anchored case + end else begin + norm_shamt = addend_shamt_q; // Undo the initial shift + normalized_exponent = tentative_exponent_q; + end + end + + // Do the large normalization shift + assign sum_shifted = sum_q << norm_shamt; + + // The addend-anchored case needs a 1-bit normalization since the leading-one can be to the left + // or right of the (non-carry) MSB of the sum. + always_comb begin : small_norm + // Default assignment, discarding carry bit + {final_mantissa, sum_sticky_bits} = sum_shifted; + final_exponent = normalized_exponent; + + // The normalized sum has overflown, align right and fix exponent + if (sum_shifted[3*PRECISION_BITS+4]) begin // check the carry bit + {final_mantissa, sum_sticky_bits} = sum_shifted >> 1; + final_exponent = normalized_exponent + 1; + // The normalized sum is normal, nothing to do + end else if (sum_shifted[3*PRECISION_BITS+3]) begin // check the sum MSB + // do nothing + // The normalized sum is still denormal, align left - unless the result is not already subnormal + end else if (normalized_exponent > 1) begin + {final_mantissa, sum_sticky_bits} = sum_shifted << 1; + final_exponent = normalized_exponent - 1; + // Otherwise we're denormal + end else begin + final_exponent = '0; + end + end + + // Update the sticky bit with the shifted-out bits + assign sticky_after_norm = (| {sum_sticky_bits}) | sticky_before_add_q; + + // ---------------------------- + // Rounding and classification + // ---------------------------- + logic pre_round_sign; + logic [SUPER_EXP_BITS+SUPER_MAN_BITS-1:0] pre_round_abs; // absolute value of result before rounding + logic [1:0] round_sticky_bits; + + logic of_before_round, of_after_round; // overflow + logic uf_before_round, uf_after_round; // underflow + + logic [NUM_FORMATS-1:0][SUPER_EXP_BITS+SUPER_MAN_BITS-1:0] fmt_pre_round_abs; // per format + logic [NUM_FORMATS-1:0][1:0] fmt_round_sticky_bits; + + logic [NUM_FORMATS-1:0] fmt_of_after_round; + logic [NUM_FORMATS-1:0] fmt_uf_after_round; + + logic rounded_sign; + logic [SUPER_EXP_BITS+SUPER_MAN_BITS-1:0] rounded_abs; // absolute value of result after rounding + logic result_zero; + + // Classification before round. RISC-V mandates checking underflow AFTER rounding! + assign of_before_round = final_exponent >= 2**(fpnew_pkg::exp_bits(dst_fmt_q2))-1; // infinity exponent is all ones + assign uf_before_round = final_exponent == 0; // exponent for subnormals capped to 0 + + // Pack exponent and mantissa into proper rounding form + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_res_assemble + // Set up some constants + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + logic [EXP_BITS-1:0] pre_round_exponent; + logic [MAN_BITS-1:0] pre_round_mantissa; + + if (FpFmtConfig[fmt]) begin : active_format + + assign pre_round_exponent = (of_before_round) ? 2**EXP_BITS-2 : final_exponent[EXP_BITS-1:0]; + assign pre_round_mantissa = (of_before_round) ? '1 : final_mantissa[SUPER_MAN_BITS-:MAN_BITS]; + // Assemble result before rounding. In case of overflow, the largest normal value is set. + assign fmt_pre_round_abs[fmt] = {pre_round_exponent, pre_round_mantissa}; // 0-extend + + // Round bit is after mantissa (1 in case of overflow for rounding) + assign fmt_round_sticky_bits[fmt][1] = final_mantissa[SUPER_MAN_BITS-MAN_BITS] | + of_before_round; + + // remaining bits in mantissa to sticky (1 in case of overflow for rounding) + if (MAN_BITS < SUPER_MAN_BITS) begin : narrow_sticky + assign fmt_round_sticky_bits[fmt][0] = (| final_mantissa[SUPER_MAN_BITS-MAN_BITS-1:0]) | + sticky_after_norm | of_before_round; + end else begin : normal_sticky + assign fmt_round_sticky_bits[fmt][0] = sticky_after_norm | of_before_round; + end + end else begin : inactive_format + assign fmt_pre_round_abs[fmt] = '{default: fpnew_pkg::DONT_CARE}; + assign fmt_round_sticky_bits[fmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Assemble result before rounding. In case of overflow, the largest normal value is set. + assign pre_round_sign = final_sign_q; + assign pre_round_abs = fmt_pre_round_abs[dst_fmt_q2]; + + // In case of overflow, the round and sticky bits are set for proper rounding + assign round_sticky_bits = fmt_round_sticky_bits[dst_fmt_q2]; + + // Perform the rounding + fpnew_rounding #( + .AbsWidth ( SUPER_EXP_BITS + SUPER_MAN_BITS ) + ) i_fpnew_rounding ( + .abs_value_i ( pre_round_abs ), + .sign_i ( pre_round_sign ), + .round_sticky_bits_i ( round_sticky_bits ), + .rnd_mode_i ( rnd_mode_q ), + .effective_subtraction_i ( effective_subtraction_q ), + .abs_rounded_o ( rounded_abs ), + .sign_o ( rounded_sign ), + .exact_zero_o ( result_zero ) + ); + + logic [NUM_FORMATS-1:0][WIDTH-1:0] fmt_result; + + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_sign_inject + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(fpnew_pkg::fp_format_e'(fmt)); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(fpnew_pkg::fp_format_e'(fmt)); + + if (FpFmtConfig[fmt]) begin : active_format + always_comb begin : post_process + // detect of / uf + fmt_uf_after_round[fmt] = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '0; // denormal + fmt_of_after_round[fmt] = rounded_abs[EXP_BITS+MAN_BITS-1:MAN_BITS] == '1; // inf exp. + + // Assemble regular result, nan box short ones. + fmt_result[fmt] = '1; + fmt_result[fmt][FP_WIDTH-1:0] = {rounded_sign, rounded_abs[EXP_BITS+MAN_BITS-1:0]}; + end + end else begin : inactive_format + assign fmt_uf_after_round[fmt] = fpnew_pkg::DONT_CARE; + assign fmt_of_after_round[fmt] = fpnew_pkg::DONT_CARE; + assign fmt_result[fmt] = '{default: fpnew_pkg::DONT_CARE}; + end + end + + // Classification after rounding select by destination format + assign uf_after_round = fmt_uf_after_round[dst_fmt_q2]; + assign of_after_round = fmt_of_after_round[dst_fmt_q2]; + + + // ----------------- + // Result selection + // ----------------- + logic [WIDTH-1:0] regular_result; + fpnew_pkg::status_t regular_status; + + // Assemble regular result + assign regular_result = fmt_result[dst_fmt_q2]; + assign regular_status.NV = 1'b0; // only valid cases are handled in regular path + assign regular_status.DZ = 1'b0; // no divisions + assign regular_status.OF = of_before_round | of_after_round; // rounding can introduce overflow + assign regular_status.UF = uf_after_round & regular_status.NX; // only inexact results raise UF + assign regular_status.NX = (| round_sticky_bits) | of_before_round | of_after_round; + + // Final results for output pipeline + logic [WIDTH-1:0] result_d; + fpnew_pkg::status_t status_d; + + // Select output depending on special case detection + assign result_d = result_is_special_q ? special_result_q : regular_result; + assign status_d = result_is_special_q ? special_status_q : regular_status; + + // ---------------- + // Output Pipeline + // ---------------- + // Output pipeline signals, index i holds signal after i register stages + logic [0:NUM_OUT_REGS][WIDTH-1:0] out_pipe_result_q; + fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q; + TagType [0:NUM_OUT_REGS] out_pipe_tag_q; + AuxType [0:NUM_OUT_REGS] out_pipe_aux_q; + logic [0:NUM_OUT_REGS] out_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_OUT_REGS] out_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign out_pipe_result_q[0] = result_d; + assign out_pipe_status_q[0] = status_d; + assign out_pipe_tag_q[0] = mid_pipe_tag_q[NUM_MID_REGS]; + assign out_pipe_aux_q[0] = mid_pipe_aux_q[NUM_MID_REGS]; + assign out_pipe_valid_q[0] = mid_pipe_valid_q[NUM_MID_REGS]; + // Input stage: Propagate pipeline ready signal to inside pipe + assign mid_pipe_ready[NUM_MID_REGS] = out_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_OUT_REGS; i++) begin : gen_output_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign out_pipe_ready[i] = out_pipe_ready[i+1] | ~out_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(out_pipe_valid_q[i+1], out_pipe_valid_q[i], out_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = out_pipe_ready[i] & out_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(out_pipe_result_q[i+1], out_pipe_result_q[i], reg_ena, '0) + `FFL(out_pipe_status_q[i+1], out_pipe_status_q[i], reg_ena, '0) + `FFL(out_pipe_tag_q[i+1], out_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(out_pipe_aux_q[i+1], out_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: Ready travels backwards from output side, driven by downstream circuitry + assign out_pipe_ready[NUM_OUT_REGS] = out_ready_i; + // Output stage: assign module outputs + assign result_o = out_pipe_result_q[NUM_OUT_REGS]; + assign status_o = out_pipe_status_q[NUM_OUT_REGS]; + assign extension_bit_o = 1'b1; // always NaN-Box result + assign tag_o = out_pipe_tag_q[NUM_OUT_REGS]; + assign aux_o = out_pipe_aux_q[NUM_OUT_REGS]; + assign out_valid_o = out_pipe_valid_q[NUM_OUT_REGS]; + assign busy_o = (| {inp_pipe_valid_q, mid_pipe_valid_q, out_pipe_valid_q}); +endmodule
diff --git a/verilog/rtl/fpnew_noncomp.sv b/verilog/rtl/fpnew_noncomp.sv new file mode 100644 index 0000000..acddd48 --- /dev/null +++ b/verilog/rtl/fpnew_noncomp.sv
@@ -0,0 +1,403 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + + +module fpnew_noncomp #( + parameter fpnew_pkg::fp_format_e FpFormat = fpnew_pkg::fp_format_e'(0), + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + parameter type AuxType = logic, + + localparam int unsigned WIDTH = fpnew_pkg::fp_width(FpFormat) // do not change +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [1:0][WIDTH-1:0] operands_i, // 2 operands + input logic [1:0] is_boxed_i, // 2 operands + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input TagType tag_i, + input AuxType aux_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [WIDTH-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output fpnew_pkg::classmask_e class_mask_o, + output logic is_class_o, + output TagType tag_o, + output AuxType aux_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + // ---------- + // Constants + // ---------- + localparam int unsigned EXP_BITS = fpnew_pkg::exp_bits(FpFormat); + localparam int unsigned MAN_BITS = fpnew_pkg::man_bits(FpFormat); + // Pipelines + localparam NUM_INP_REGS = (PipeConfig == fpnew_pkg::BEFORE || PipeConfig == fpnew_pkg::INSIDE) + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? ((NumPipeRegs + 1) / 2) // First to get distributed regs + : 0); // no regs here otherwise + localparam NUM_OUT_REGS = PipeConfig == fpnew_pkg::AFTER + ? NumPipeRegs + : (PipeConfig == fpnew_pkg::DISTRIBUTED + ? (NumPipeRegs / 2) // Last to get distributed regs + : 0); // no regs here otherwise + + // ---------------- + // Type definition + // ---------------- + typedef struct packed { + logic sign; + logic [EXP_BITS-1:0] exponent; + logic [MAN_BITS-1:0] mantissa; + } fp_t; + + // --------------- + // Input pipeline + // --------------- + // Input pipeline signals, index i holds signal after i register stages + logic [0:NUM_INP_REGS][1:0][WIDTH-1:0] inp_pipe_operands_q; + logic [0:NUM_INP_REGS][1:0] inp_pipe_is_boxed_q; + fpnew_pkg::roundmode_e [0:NUM_INP_REGS] inp_pipe_rnd_mode_q; + fpnew_pkg::operation_e [0:NUM_INP_REGS] inp_pipe_op_q; + logic [0:NUM_INP_REGS] inp_pipe_op_mod_q; + TagType [0:NUM_INP_REGS] inp_pipe_tag_q; + AuxType [0:NUM_INP_REGS] inp_pipe_aux_q; + logic [0:NUM_INP_REGS] inp_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_INP_REGS] inp_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign inp_pipe_operands_q[0] = operands_i; + assign inp_pipe_is_boxed_q[0] = is_boxed_i; + assign inp_pipe_rnd_mode_q[0] = rnd_mode_i; + assign inp_pipe_op_q[0] = op_i; + assign inp_pipe_op_mod_q[0] = op_mod_i; + assign inp_pipe_tag_q[0] = tag_i; + assign inp_pipe_aux_q[0] = aux_i; + assign inp_pipe_valid_q[0] = in_valid_i; + // Input stage: Propagate pipeline ready signal to updtream circuitry + assign in_ready_o = inp_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_INP_REGS; i++) begin : gen_input_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign inp_pipe_ready[i] = inp_pipe_ready[i+1] | ~inp_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(inp_pipe_valid_q[i+1], inp_pipe_valid_q[i], inp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = inp_pipe_ready[i] & inp_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(inp_pipe_operands_q[i+1], inp_pipe_operands_q[i], reg_ena, '0) + `FFL(inp_pipe_is_boxed_q[i+1], inp_pipe_is_boxed_q[i], reg_ena, '0) + `FFL(inp_pipe_rnd_mode_q[i+1], inp_pipe_rnd_mode_q[i], reg_ena, fpnew_pkg::RNE) + `FFL(inp_pipe_op_q[i+1], inp_pipe_op_q[i], reg_ena, fpnew_pkg::FMADD) + `FFL(inp_pipe_op_mod_q[i+1], inp_pipe_op_mod_q[i], reg_ena, '0) + `FFL(inp_pipe_tag_q[i+1], inp_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(inp_pipe_aux_q[i+1], inp_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + + // --------------------- + // Input classification + // --------------------- + fpnew_pkg::fp_info_t [1:0] info_q; + + // Classify input + fpnew_classifier #( + .FpFormat ( FpFormat ), + .NumOperands ( 2 ) + ) i_class_a ( + .operands_i ( inp_pipe_operands_q[NUM_INP_REGS] ), + .is_boxed_i ( inp_pipe_is_boxed_q[NUM_INP_REGS] ), + .info_o ( info_q ) + ); + + fp_t operand_a, operand_b; + fpnew_pkg::fp_info_t info_a, info_b; + + // Packing-order-agnostic assignments + assign operand_a = inp_pipe_operands_q[NUM_INP_REGS][0]; + assign operand_b = inp_pipe_operands_q[NUM_INP_REGS][1]; + assign info_a = info_q[0]; + assign info_b = info_q[1]; + + logic any_operand_inf; + logic any_operand_nan; + logic signalling_nan; + + // Reduction for special case handling + assign any_operand_inf = (| {info_a.is_inf, info_b.is_inf}); + assign any_operand_nan = (| {info_a.is_nan, info_b.is_nan}); + assign signalling_nan = (| {info_a.is_signalling, info_b.is_signalling}); + + logic operands_equal, operand_a_smaller; + + // Equality checks for zeroes too + assign operands_equal = (operand_a == operand_b) || (info_a.is_zero && info_b.is_zero); + // Invert result if non-zero signs involved (unsigned comparison) + assign operand_a_smaller = (operand_a < operand_b) ^ (operand_a.sign || operand_b.sign); + + // --------------- + // Sign Injection + // --------------- + fp_t sgnj_result; + fpnew_pkg::status_t sgnj_status; + logic sgnj_extension_bit; + + // Sign Injection - operation is encoded in rnd_mode_q: + // RNE = SGNJ, RTZ = SGNJN, RDN = SGNJX, RUP = Passthrough (no NaN-box check) + always_comb begin : sign_injections + logic sign_a, sign_b; // internal signs + // Default assignment + sgnj_result = operand_a; // result based on operand a + + // NaN-boxing check will treat invalid inputs as canonical NaNs + if (!info_a.is_boxed) sgnj_result = '{sign: 1'b0, exponent: '1, mantissa: 2**(MAN_BITS-1)}; + + // Internal signs are treated as positive in case of non-NaN-boxed values + sign_a = operand_a.sign & info_a.is_boxed; + sign_b = operand_b.sign & info_b.is_boxed; + + // Do the sign injection based on rm field + unique case (inp_pipe_rnd_mode_q[NUM_INP_REGS]) + fpnew_pkg::RNE: sgnj_result.sign = sign_b; // SGNJ + fpnew_pkg::RTZ: sgnj_result.sign = ~sign_b; // SGNJN + fpnew_pkg::RDN: sgnj_result.sign = sign_a ^ sign_b; // SGNJX + fpnew_pkg::RUP: sgnj_result = operand_a; // passthrough + default: sgnj_result = '{default: fpnew_pkg::DONT_CARE}; // don't care + endcase + end + + assign sgnj_status = '0; // sign injections never raise exceptions + + // op_mod_q enables integer sign-extension of result (for storing to integer regfile) + assign sgnj_extension_bit = inp_pipe_op_mod_q[NUM_INP_REGS] ? sgnj_result.sign : 1'b1; + + // ------------------ + // Minimum / Maximum + // ------------------ + fp_t minmax_result; + fpnew_pkg::status_t minmax_status; + logic minmax_extension_bit; + + // Minimum/Maximum - operation is encoded in rnd_mode_q: + // RNE = MIN, RTZ = MAX + always_comb begin : min_max + // Default assignment + minmax_status = '0; + + // Min/Max use quiet comparisons - only sNaN are invalid + minmax_status.NV = signalling_nan; + + // Both NaN inputs cause a NaN output + if (info_a.is_nan && info_b.is_nan) + minmax_result = '{sign: 1'b0, exponent: '1, mantissa: 2**(MAN_BITS-1)}; // canonical qNaN + // If one operand is NaN, the non-NaN operand is returned + else if (info_a.is_nan) minmax_result = operand_b; + else if (info_b.is_nan) minmax_result = operand_a; + // Otherwise decide according to the operation + else begin + unique case (inp_pipe_rnd_mode_q[NUM_INP_REGS]) + fpnew_pkg::RNE: minmax_result = operand_a_smaller ? operand_a : operand_b; // MIN + fpnew_pkg::RTZ: minmax_result = operand_a_smaller ? operand_b : operand_a; // MAX + default: minmax_result = '{default: fpnew_pkg::DONT_CARE}; // don't care + endcase + end + end + + assign minmax_extension_bit = 1'b1; // NaN-box as result is always a float value + + // ------------ + // Comparisons + // ------------ + fp_t cmp_result; + fpnew_pkg::status_t cmp_status; + logic cmp_extension_bit; + + // Comparisons - operation is encoded in rnd_mode_q: + // RNE = LE, RTZ = LT, RDN = EQ + // op_mod_q inverts boolean outputs + always_comb begin : comparisons + // Default assignment + cmp_result = '0; // false + cmp_status = '0; // no flags + + // Signalling NaNs always compare as false and are illegal + if (signalling_nan) cmp_status.NV = 1'b1; // invalid operation + // Otherwise do comparisons + else begin + unique case (inp_pipe_rnd_mode_q[NUM_INP_REGS]) + fpnew_pkg::RNE: begin // Less than or equal + if (any_operand_nan) cmp_status.NV = 1'b1; // Signalling comparison: NaNs are invalid + else cmp_result = (operand_a_smaller | operands_equal) ^ inp_pipe_op_mod_q[NUM_INP_REGS]; + end + fpnew_pkg::RTZ: begin // Less than + if (any_operand_nan) cmp_status.NV = 1'b1; // Signalling comparison: NaNs are invalid + else cmp_result = (operand_a_smaller & ~operands_equal) ^ inp_pipe_op_mod_q[NUM_INP_REGS]; + end + fpnew_pkg::RDN: begin // Equal + if (any_operand_nan) cmp_result = inp_pipe_op_mod_q[NUM_INP_REGS]; // NaN always not equal + else cmp_result = operands_equal ^ inp_pipe_op_mod_q[NUM_INP_REGS]; + end + default: cmp_result = '{default: fpnew_pkg::DONT_CARE}; // don't care + endcase + end + end + + assign cmp_extension_bit = 1'b0; // Comparisons always produce booleans in integer registers + + // --------------- + // Classification + // --------------- + fpnew_pkg::status_t class_status; + logic class_extension_bit; + fpnew_pkg::classmask_e class_mask_d; // the result is actually here + + // Classification - always return the classification mask on the dedicated port + always_comb begin : classify + if (info_a.is_normal) begin + class_mask_d = operand_a.sign ? fpnew_pkg::NEGNORM : fpnew_pkg::POSNORM; + end else if (info_a.is_subnormal) begin + class_mask_d = operand_a.sign ? fpnew_pkg::NEGSUBNORM : fpnew_pkg::POSSUBNORM; + end else if (info_a.is_zero) begin + class_mask_d = operand_a.sign ? fpnew_pkg::NEGZERO : fpnew_pkg::POSZERO; + end else if (info_a.is_inf) begin + class_mask_d = operand_a.sign ? fpnew_pkg::NEGINF : fpnew_pkg::POSINF; + end else if (info_a.is_nan) begin + class_mask_d = info_a.is_signalling ? fpnew_pkg::SNAN : fpnew_pkg::QNAN; + end else begin + class_mask_d = fpnew_pkg::QNAN; // default value + end + end + + assign class_status = '0; // classification does not set flags + assign class_extension_bit = 1'b0; // classification always produces results in integer registers + + // ----------------- + // Result selection + // ----------------- + fp_t result_d; + fpnew_pkg::status_t status_d; + logic extension_bit_d; + logic is_class_d; + + // Select result + always_comb begin : select_result + unique case (inp_pipe_op_q[NUM_INP_REGS]) + fpnew_pkg::SGNJ: begin + result_d = sgnj_result; + status_d = sgnj_status; + extension_bit_d = sgnj_extension_bit; + end + fpnew_pkg::MINMAX: begin + result_d = minmax_result; + status_d = minmax_status; + extension_bit_d = minmax_extension_bit; + end + fpnew_pkg::CMP: begin + result_d = cmp_result; + status_d = cmp_status; + extension_bit_d = cmp_extension_bit; + end + fpnew_pkg::CLASSIFY: begin + result_d = '{default: fpnew_pkg::DONT_CARE}; // unused + status_d = class_status; + extension_bit_d = class_extension_bit; + end + default: begin + result_d = '{default: fpnew_pkg::DONT_CARE}; // dont care + status_d = '{default: fpnew_pkg::DONT_CARE}; // dont care + extension_bit_d = fpnew_pkg::DONT_CARE; // dont care + end + endcase + end + + assign is_class_d = (inp_pipe_op_q[NUM_INP_REGS] == fpnew_pkg::CLASSIFY); + + // ---------------- + // Output Pipeline + // ---------------- + // Output pipeline signals, index i holds signal after i register stages + fp_t [0:NUM_OUT_REGS] out_pipe_result_q; + fpnew_pkg::status_t [0:NUM_OUT_REGS] out_pipe_status_q; + logic [0:NUM_OUT_REGS] out_pipe_extension_bit_q; + fpnew_pkg::classmask_e [0:NUM_OUT_REGS] out_pipe_class_mask_q; + logic [0:NUM_OUT_REGS] out_pipe_is_class_q; + TagType [0:NUM_OUT_REGS] out_pipe_tag_q; + AuxType [0:NUM_OUT_REGS] out_pipe_aux_q; + logic [0:NUM_OUT_REGS] out_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NUM_OUT_REGS] out_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign out_pipe_result_q[0] = result_d; + assign out_pipe_status_q[0] = status_d; + assign out_pipe_extension_bit_q[0] = extension_bit_d; + assign out_pipe_class_mask_q[0] = class_mask_d; + assign out_pipe_is_class_q[0] = is_class_d; + assign out_pipe_tag_q[0] = inp_pipe_tag_q[NUM_INP_REGS]; + assign out_pipe_aux_q[0] = inp_pipe_aux_q[NUM_INP_REGS]; + assign out_pipe_valid_q[0] = inp_pipe_valid_q[NUM_INP_REGS]; + // Input stage: Propagate pipeline ready signal to inside pipe + assign inp_pipe_ready[NUM_INP_REGS] = out_pipe_ready[0]; + // Generate the register stages + for (genvar i = 0; i < NUM_OUT_REGS; i++) begin : gen_output_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign out_pipe_ready[i] = out_pipe_ready[i+1] | ~out_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(out_pipe_valid_q[i+1], out_pipe_valid_q[i], out_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = out_pipe_ready[i] & out_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(out_pipe_result_q[i+1], out_pipe_result_q[i], reg_ena, '0) + `FFL(out_pipe_status_q[i+1], out_pipe_status_q[i], reg_ena, '0) + `FFL(out_pipe_extension_bit_q[i+1], out_pipe_extension_bit_q[i], reg_ena, '0) + `FFL(out_pipe_class_mask_q[i+1], out_pipe_class_mask_q[i], reg_ena, fpnew_pkg::QNAN) + `FFL(out_pipe_is_class_q[i+1], out_pipe_is_class_q[i], reg_ena, '0) + `FFL(out_pipe_tag_q[i+1], out_pipe_tag_q[i], reg_ena, TagType'('0)) + `FFL(out_pipe_aux_q[i+1], out_pipe_aux_q[i], reg_ena, AuxType'('0)) + end + // Output stage: Ready travels backwards from output side, driven by downstream circuitry + assign out_pipe_ready[NUM_OUT_REGS] = out_ready_i; + // Output stage: assign module outputs + assign result_o = out_pipe_result_q[NUM_OUT_REGS]; + assign status_o = out_pipe_status_q[NUM_OUT_REGS]; + assign extension_bit_o = out_pipe_extension_bit_q[NUM_OUT_REGS]; + assign class_mask_o = out_pipe_class_mask_q[NUM_OUT_REGS]; + assign is_class_o = out_pipe_is_class_q[NUM_OUT_REGS]; + assign tag_o = out_pipe_tag_q[NUM_OUT_REGS]; + assign aux_o = out_pipe_aux_q[NUM_OUT_REGS]; + assign out_valid_o = out_pipe_valid_q[NUM_OUT_REGS]; + assign busy_o = (| {inp_pipe_valid_q, out_pipe_valid_q}); +endmodule
diff --git a/verilog/rtl/fpnew_opgroup_block.sv b/verilog/rtl/fpnew_opgroup_block.sv new file mode 100644 index 0000000..e3be31d --- /dev/null +++ b/verilog/rtl/fpnew_opgroup_block.sv
@@ -0,0 +1,230 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_opgroup_block #( + parameter fpnew_pkg::opgroup_e OpGroup = fpnew_pkg::ADDMUL, + // FPU configuration + parameter int unsigned Width = 32, + parameter logic EnableVectors = 1'b1, + parameter fpnew_pkg::fmt_logic_t FpFmtMask = '1, + parameter fpnew_pkg::ifmt_logic_t IntFmtMask = '1, + parameter fpnew_pkg::fmt_unsigned_t FmtPipeRegs = '{default: 0}, + parameter fpnew_pkg::fmt_unit_types_t FmtUnitTypes = '{default: fpnew_pkg::PARALLEL}, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + // Do not change + localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS, + localparam int unsigned NUM_OPERANDS = fpnew_pkg::num_operands(OpGroup) +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [NUM_OPERANDS-1:0][Width-1:0] operands_i, + input logic [NUM_FORMATS-1:0][NUM_OPERANDS-1:0] is_boxed_i, + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input fpnew_pkg::fp_format_e src_fmt_i, + input fpnew_pkg::fp_format_e dst_fmt_i, + input fpnew_pkg::int_format_e int_fmt_i, + input logic vectorial_op_i, + input TagType tag_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [Width-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + // ---------------- + // Type Definition + // ---------------- + typedef struct packed { + logic [Width-1:0] result; + fpnew_pkg::status_t status; + logic ext_bit; + TagType tag; + } output_t; + + // Handshake signals for the slices + logic [NUM_FORMATS-1:0] fmt_in_ready, fmt_out_valid, fmt_out_ready, fmt_busy; + output_t [NUM_FORMATS-1:0] fmt_outputs; + + // ----------- + // Input Side + // ----------- + assign in_ready_o = in_valid_i & fmt_in_ready[dst_fmt_i]; // Ready is given by selected format + + // ------------------------- + // Generate Parallel Slices + // ------------------------- + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_parallel_slices + // Some constants for this format + localparam logic ANY_MERGED = fpnew_pkg::any_enabled_multi(FmtUnitTypes, FpFmtMask); + localparam logic IS_FIRST_MERGED = + fpnew_pkg::is_first_enabled_multi(fpnew_pkg::fp_format_e'(fmt), FmtUnitTypes, FpFmtMask); + + // Generate slice only if format enabled + if (FpFmtMask[fmt] && (FmtUnitTypes[fmt] == fpnew_pkg::PARALLEL)) begin : active_format + + logic in_valid; + + assign in_valid = in_valid_i & (dst_fmt_i == fmt); // enable selected format + + fpnew_opgroup_fmt_slice #( + .OpGroup ( OpGroup ), + .FpFormat ( fpnew_pkg::fp_format_e'(fmt) ), + .Width ( Width ), + .EnableVectors ( EnableVectors ), + .NumPipeRegs ( FmtPipeRegs[fmt] ), + .PipeConfig ( PipeConfig ), + .TagType ( TagType ) + ) i_fmt_slice ( + .clk_i, + .rst_ni, + .operands_i ( operands_i ), + .is_boxed_i ( is_boxed_i[fmt] ), + .rnd_mode_i, + .op_i, + .op_mod_i, + .vectorial_op_i, + .tag_i, + .in_valid_i ( in_valid ), + .in_ready_o ( fmt_in_ready[fmt] ), + .flush_i, + .result_o ( fmt_outputs[fmt].result ), + .status_o ( fmt_outputs[fmt].status ), + .extension_bit_o( fmt_outputs[fmt].ext_bit ), + .tag_o ( fmt_outputs[fmt].tag ), + .out_valid_o ( fmt_out_valid[fmt] ), + .out_ready_i ( fmt_out_ready[fmt] ), + .busy_o ( fmt_busy[fmt] ) + ); + // If the format wants to use merged ops, tie off the dangling ones not used here + end else if (FpFmtMask[fmt] && ANY_MERGED && !IS_FIRST_MERGED) begin : merged_unused + + localparam FMT = fpnew_pkg::get_first_enabled_multi(FmtUnitTypes, FpFmtMask); + // Ready is split up into formats + assign fmt_in_ready[fmt] = fmt_in_ready[int'(FMT)]; + + assign fmt_out_valid[fmt] = 1'b0; // don't emit values + assign fmt_busy[fmt] = 1'b0; // never busy + // Outputs are don't care + assign fmt_outputs[fmt].result = '{default: fpnew_pkg::DONT_CARE}; + assign fmt_outputs[fmt].status = '{default: fpnew_pkg::DONT_CARE}; + assign fmt_outputs[fmt].ext_bit = fpnew_pkg::DONT_CARE; + assign fmt_outputs[fmt].tag = TagType'(fpnew_pkg::DONT_CARE); + + // Tie off disabled formats + end else if (!FpFmtMask[fmt] || (FmtUnitTypes[fmt] == fpnew_pkg::DISABLED)) begin : disable_fmt + assign fmt_in_ready[fmt] = 1'b0; // don't accept operations + assign fmt_out_valid[fmt] = 1'b0; // don't emit values + assign fmt_busy[fmt] = 1'b0; // never busy + // Outputs are don't care + assign fmt_outputs[fmt].result = '{default: fpnew_pkg::DONT_CARE}; + assign fmt_outputs[fmt].status = '{default: fpnew_pkg::DONT_CARE}; + assign fmt_outputs[fmt].ext_bit = fpnew_pkg::DONT_CARE; + assign fmt_outputs[fmt].tag = TagType'(fpnew_pkg::DONT_CARE); + end + end + + // ---------------------- + // Generate Merged Slice + // ---------------------- + if (fpnew_pkg::any_enabled_multi(FmtUnitTypes, FpFmtMask)) begin : gen_merged_slice + + localparam FMT = fpnew_pkg::get_first_enabled_multi(FmtUnitTypes, FpFmtMask); + localparam REG = fpnew_pkg::get_num_regs_multi(FmtPipeRegs, FmtUnitTypes, FpFmtMask); + + logic in_valid; + + assign in_valid = in_valid_i & (FmtUnitTypes[dst_fmt_i] == fpnew_pkg::MERGED); + + fpnew_opgroup_multifmt_slice #( + .OpGroup ( OpGroup ), + .Width ( Width ), + .FpFmtConfig ( FpFmtMask ), + .IntFmtConfig ( IntFmtMask ), + .EnableVectors ( EnableVectors ), + .NumPipeRegs ( REG ), + .PipeConfig ( PipeConfig ), + .TagType ( TagType ) + ) i_multifmt_slice ( + .clk_i, + .rst_ni, + .operands_i, + .is_boxed_i, + .rnd_mode_i, + .op_i, + .op_mod_i, + .src_fmt_i, + .dst_fmt_i, + .int_fmt_i, + .vectorial_op_i, + .tag_i, + .in_valid_i ( in_valid ), + .in_ready_o ( fmt_in_ready[FMT] ), + .flush_i, + .result_o ( fmt_outputs[FMT].result ), + .status_o ( fmt_outputs[FMT].status ), + .extension_bit_o ( fmt_outputs[FMT].ext_bit ), + .tag_o ( fmt_outputs[FMT].tag ), + .out_valid_o ( fmt_out_valid[FMT] ), + .out_ready_i ( fmt_out_ready[FMT] ), + .busy_o ( fmt_busy[FMT] ) + ); + + end + + // ------------------ + // Arbitrate Outputs + // ------------------ + output_t arbiter_output; + + // Round-Robin arbiter to decide which result to use + rr_arb_tree #( + .NumIn ( NUM_FORMATS ), + .DataType ( output_t ), + .AxiVldRdy ( 1'b1 ) + ) i_arbiter ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ( '0 ), + .req_i ( fmt_out_valid ), + .gnt_o ( fmt_out_ready ), + .data_i ( fmt_outputs ), + .gnt_i ( out_ready_i ), + .req_o ( out_valid_o ), + .data_o ( arbiter_output ), + .idx_o ( /* unused */ ) + ); + + // Unpack output + assign result_o = arbiter_output.result; + assign status_o = arbiter_output.status; + assign extension_bit_o = arbiter_output.ext_bit; + assign tag_o = arbiter_output.tag; + + assign busy_o = (| fmt_busy); + +endmodule
diff --git a/verilog/rtl/fpnew_opgroup_fmt_slice.sv b/verilog/rtl/fpnew_opgroup_fmt_slice.sv new file mode 100644 index 0000000..fda2a57 --- /dev/null +++ b/verilog/rtl/fpnew_opgroup_fmt_slice.sv
@@ -0,0 +1,276 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_opgroup_fmt_slice #( + parameter fpnew_pkg::opgroup_e OpGroup = fpnew_pkg::ADDMUL, + parameter fpnew_pkg::fp_format_e FpFormat = fpnew_pkg::fp_format_e'(0), + // FPU configuration + parameter int unsigned Width = 32, + parameter logic EnableVectors = 1'b1, + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + // Do not change + localparam int unsigned NUM_OPERANDS = fpnew_pkg::num_operands(OpGroup) +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [NUM_OPERANDS-1:0][Width-1:0] operands_i, + input logic [NUM_OPERANDS-1:0] is_boxed_i, + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input logic vectorial_op_i, + input TagType tag_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [Width-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(FpFormat); + localparam int unsigned NUM_LANES = fpnew_pkg::num_lanes(Width, FpFormat, EnableVectors); + + + logic [NUM_LANES-1:0] lane_in_ready, lane_out_valid; // Handshake signals for the lanes + logic vectorial_op; + + logic [NUM_LANES*FP_WIDTH-1:0] slice_result; + logic [Width-1:0] slice_regular_result, slice_class_result, slice_vec_class_result; + + fpnew_pkg::status_t [NUM_LANES-1:0] lane_status; + logic [NUM_LANES-1:0] lane_ext_bit; // only the first one is actually used + fpnew_pkg::classmask_e [NUM_LANES-1:0] lane_class_mask; + TagType [NUM_LANES-1:0] lane_tags; // only the first one is actually used + logic [NUM_LANES-1:0] lane_vectorial, lane_busy, lane_is_class; // dito + + logic result_is_vector, result_is_class; + + // ----------- + // Input Side + // ----------- + assign in_ready_o = lane_in_ready[0]; // Upstream ready is given by first lane + assign vectorial_op = vectorial_op_i & EnableVectors; // only do vectorial stuff if enabled + + // --------------- + // Generate Lanes + // --------------- + for (genvar lane = 0; lane < int'(NUM_LANES); lane++) begin : gen_num_lanes + logic [FP_WIDTH-1:0] local_result; // lane-local results + logic local_sign; + + // Generate instances only if needed, lane 0 always generated + if ((lane == 0) || EnableVectors) begin : active_lane + logic in_valid, out_valid, out_ready; // lane-local handshake + + logic [NUM_OPERANDS-1:0][FP_WIDTH-1:0] local_operands; // lane-local operands + logic [FP_WIDTH-1:0] op_result; // lane-local results + fpnew_pkg::status_t op_status; + + assign in_valid = in_valid_i & ((lane == 0) | vectorial_op); // upper lanes only for vectors + // Slice out the operands for this lane + always_comb begin : prepare_input + for (int i = 0; i < int'(NUM_OPERANDS); i++) begin + local_operands[i] = operands_i[i][(unsigned'(lane)+1)*FP_WIDTH-1:unsigned'(lane)*FP_WIDTH]; + end + end + + // Instantiate the operation from the selected opgroup + if (OpGroup == fpnew_pkg::ADDMUL) begin : lane_instance + fpnew_fma #( + .FpFormat ( FpFormat ), + .NumPipeRegs ( NumPipeRegs ), + .PipeConfig ( PipeConfig ), + .TagType ( TagType ), + .AuxType ( logic ) + ) i_fma ( + .clk_i, + .rst_ni, + .operands_i ( local_operands ), + .is_boxed_i ( is_boxed_i[NUM_OPERANDS-1:0] ), + .rnd_mode_i, + .op_i, + .op_mod_i, + .tag_i, + .aux_i ( vectorial_op ), // Remember whether operation was vectorial + .in_valid_i ( in_valid ), + .in_ready_o ( lane_in_ready[lane] ), + .flush_i, + .result_o ( op_result ), + .status_o ( op_status ), + .extension_bit_o ( lane_ext_bit[lane] ), + .tag_o ( lane_tags[lane] ), + .aux_o ( lane_vectorial[lane] ), + .out_valid_o ( out_valid ), + .out_ready_i ( out_ready ), + .busy_o ( lane_busy[lane] ) + ); + assign lane_is_class[lane] = 1'b0; + assign lane_class_mask[lane] = fpnew_pkg::NEGINF; + end else if (OpGroup == fpnew_pkg::DIVSQRT) begin : lane_instance + // fpnew_divsqrt #( + // .FpFormat (FpFormat), + // .NumPipeRegs(NumPipeRegs), + // .PipeConfig (PipeConfig), + // .TagType (TagType), + // .AuxType (logic) + // ) i_divsqrt ( + // .clk_i, + // .rst_ni, + // .operands_i ( local_operands ), + // .is_boxed_i ( is_boxed_i[NUM_OPERANDS-1:0] ), + // .rnd_mode_i, + // .op_i, + // .op_mod_i, + // .tag_i, + // .aux_i ( vectorial_op ), // Remember whether operation was vectorial + // .in_valid_i ( in_valid ), + // .in_ready_o ( lane_in_ready[lane] ), + // .flush_i, + // .result_o ( op_result ), + // .status_o ( op_status ), + // .extension_bit_o ( lane_ext_bit[lane] ), + // .tag_o ( lane_tags[lane] ), + // .aux_o ( lane_vectorial[lane] ), + // .out_valid_o ( out_valid ), + // .out_ready_i ( out_ready ), + // .busy_o ( lane_busy[lane] ) + // ); + // assign lane_is_class[lane] = 1'b0; + end else if (OpGroup == fpnew_pkg::NONCOMP) begin : lane_instance + fpnew_noncomp #( + .FpFormat (FpFormat), + .NumPipeRegs(NumPipeRegs), + .PipeConfig (PipeConfig), + .TagType (TagType), + .AuxType (logic) + ) i_noncomp ( + .clk_i, + .rst_ni, + .operands_i ( local_operands ), + .is_boxed_i ( is_boxed_i[NUM_OPERANDS-1:0] ), + .rnd_mode_i, + .op_i, + .op_mod_i, + .tag_i, + .aux_i ( vectorial_op ), // Remember whether operation was vectorial + .in_valid_i ( in_valid ), + .in_ready_o ( lane_in_ready[lane] ), + .flush_i, + .result_o ( op_result ), + .status_o ( op_status ), + .extension_bit_o ( lane_ext_bit[lane] ), + .class_mask_o ( lane_class_mask[lane] ), + .is_class_o ( lane_is_class[lane] ), + .tag_o ( lane_tags[lane] ), + .aux_o ( lane_vectorial[lane] ), + .out_valid_o ( out_valid ), + .out_ready_i ( out_ready ), + .busy_o ( lane_busy[lane] ) + ); + end // ADD OTHER OPTIONS HERE + + // Handshakes are only done if the lane is actually used + assign out_ready = out_ready_i & ((lane == 0) | result_is_vector); + assign lane_out_valid[lane] = out_valid & ((lane == 0) | result_is_vector); + + // Properly NaN-box or sign-extend the slice result if not in use + assign local_result = lane_out_valid[lane] ? op_result : '{default: lane_ext_bit[0]}; + assign lane_status[lane] = lane_out_valid[lane] ? op_status : '0; + + // Otherwise generate constant sign-extension + end else begin + assign lane_out_valid[lane] = 1'b0; // unused lane + assign lane_in_ready[lane] = 1'b0; // unused lane + assign local_result = '{default: lane_ext_bit[0]}; // sign-extend/nan box + assign lane_status[lane] = '0; + assign lane_busy[lane] = 1'b0; + assign lane_is_class[lane] = 1'b0; + end + + // Insert lane result into slice result + assign slice_result[(unsigned'(lane)+1)*FP_WIDTH-1:unsigned'(lane)*FP_WIDTH] = local_result; + + // Create Classification results + if ((lane+1)*8 <= Width) begin : vectorial_class // vectorial class blocks are 8bits in size + assign local_sign = (lane_class_mask[lane] == fpnew_pkg::NEGINF || + lane_class_mask[lane] == fpnew_pkg::NEGNORM || + lane_class_mask[lane] == fpnew_pkg::NEGSUBNORM || + lane_class_mask[lane] == fpnew_pkg::NEGZERO); + // Write the current block segment + assign slice_vec_class_result[(lane+1)*8-1:lane*8] = { + local_sign, // BIT 7 + ~local_sign, // BIT 6 + lane_class_mask[lane] == fpnew_pkg::QNAN, // BIT 5 + lane_class_mask[lane] == fpnew_pkg::SNAN, // BIT 4 + lane_class_mask[lane] == fpnew_pkg::POSZERO + || lane_class_mask[lane] == fpnew_pkg::NEGZERO, // BIT 3 + lane_class_mask[lane] == fpnew_pkg::POSSUBNORM + || lane_class_mask[lane] == fpnew_pkg::NEGSUBNORM, // BIT 2 + lane_class_mask[lane] == fpnew_pkg::POSNORM + || lane_class_mask[lane] == fpnew_pkg::NEGNORM, // BIT 1 + lane_class_mask[lane] == fpnew_pkg::POSINF + || lane_class_mask[lane] == fpnew_pkg::NEGINF // BIT 0 + }; + end + end + + // ------------ + // Output Side + // ------------ + assign result_is_vector = lane_vectorial[0]; + assign result_is_class = lane_is_class[0]; + + assign slice_regular_result = $signed({extension_bit_o, slice_result}); + + localparam int unsigned CLASS_VEC_BITS = (NUM_LANES*8 > Width) ? 8 * (Width / 8) : NUM_LANES*8; + + // Pad out unused vec_class bits + if (CLASS_VEC_BITS < Width) begin : pad_vectorial_class + assign slice_vec_class_result[Width-1:CLASS_VEC_BITS] = '0; + end + + // localparam logic [Width-1:0] CLASS_VEC_MASK = 2**CLASS_VEC_BITS - 1; + + assign slice_class_result = result_is_vector ? slice_vec_class_result : lane_class_mask[0]; + + // Select the proper result + assign result_o = result_is_class ? slice_class_result : slice_regular_result; + + assign extension_bit_o = lane_ext_bit[0]; // upper lanes unused + assign tag_o = lane_tags[0]; // upper lanes unused + assign busy_o = (| lane_busy); + assign out_valid_o = lane_out_valid[0]; // upper lanes unused + + + // Collapse the lane status + always_comb begin : output_processing + // Collapse the status + automatic fpnew_pkg::status_t temp_status; + temp_status = '0; + for (int i = 0; i < int'(NUM_LANES); i++) + temp_status |= lane_status[i]; + status_o = temp_status; + end +endmodule
diff --git a/verilog/rtl/fpnew_opgroup_multifmt_slice.sv b/verilog/rtl/fpnew_opgroup_multifmt_slice.sv new file mode 100644 index 0000000..4f139e9 --- /dev/null +++ b/verilog/rtl/fpnew_opgroup_multifmt_slice.sv
@@ -0,0 +1,424 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_opgroup_multifmt_slice #( + parameter fpnew_pkg::opgroup_e OpGroup = fpnew_pkg::CONV, + parameter int unsigned Width = 64, + // FPU configuration + parameter fpnew_pkg::fmt_logic_t FpFmtConfig = '1, + parameter fpnew_pkg::ifmt_logic_t IntFmtConfig = '1, + parameter logic EnableVectors = 1'b1, + parameter int unsigned NumPipeRegs = 0, + parameter fpnew_pkg::pipe_config_t PipeConfig = fpnew_pkg::BEFORE, + parameter type TagType = logic, + // Do not change + localparam int unsigned NUM_OPERANDS = fpnew_pkg::num_operands(OpGroup), + localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [NUM_OPERANDS-1:0][Width-1:0] operands_i, + input logic [NUM_FORMATS-1:0][NUM_OPERANDS-1:0] is_boxed_i, + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input fpnew_pkg::fp_format_e src_fmt_i, + input fpnew_pkg::fp_format_e dst_fmt_i, + input fpnew_pkg::int_format_e int_fmt_i, + input logic vectorial_op_i, + input TagType tag_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [Width-1:0] result_o, + output fpnew_pkg::status_t status_o, + output logic extension_bit_o, + output TagType tag_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + localparam int unsigned MAX_FP_WIDTH = fpnew_pkg::max_fp_width(FpFmtConfig); + localparam int unsigned MAX_INT_WIDTH = fpnew_pkg::max_int_width(IntFmtConfig); + localparam int unsigned NUM_LANES = fpnew_pkg::max_num_lanes(Width, FpFmtConfig, 1'b1); + localparam int unsigned NUM_INT_FORMATS = fpnew_pkg::NUM_INT_FORMATS; + // We will send the format information along with the data + localparam int unsigned FMT_BITS = + fpnew_pkg::maximum($clog2(NUM_FORMATS), $clog2(NUM_INT_FORMATS)); + localparam int unsigned AUX_BITS = FMT_BITS + 2; // also add vectorial and integer flags + + logic [NUM_LANES-1:0] lane_in_ready, lane_out_valid; // Handshake signals for the lanes + logic vectorial_op; + logic [FMT_BITS-1:0] dst_fmt; // destination format to pass along with operation + logic [AUX_BITS-1:0] aux_data; + + // additional flags for CONV + logic dst_fmt_is_int, dst_is_cpk; + logic [1:0] dst_vec_op; // info for vectorial results (for packing) + logic [2:0] target_aux_d, target_aux_q; + logic is_up_cast, is_down_cast; + + logic [NUM_FORMATS-1:0][Width-1:0] fmt_slice_result; + logic [NUM_INT_FORMATS-1:0][Width-1:0] ifmt_slice_result; + logic [Width-1:0] conv_slice_result; + + + logic [Width-1:0] conv_target_d, conv_target_q; // vectorial conversions update a register + + fpnew_pkg::status_t [NUM_LANES-1:0] lane_status; + logic [NUM_LANES-1:0] lane_ext_bit; // only the first one is actually used + TagType [NUM_LANES-1:0] lane_tags; // only the first one is actually used + logic [NUM_LANES-1:0][AUX_BITS-1:0] lane_aux; // only the first one is actually used + logic [NUM_LANES-1:0] lane_busy; // dito + + logic result_is_vector; + logic [FMT_BITS-1:0] result_fmt; + logic result_fmt_is_int, result_is_cpk; + logic [1:0] result_vec_op; // info for vectorial results (for packing) + + // ----------- + // Input Side + // ----------- + assign in_ready_o = lane_in_ready[0]; // Upstream ready is given by first lane + assign vectorial_op = vectorial_op_i & EnableVectors; // only do vectorial stuff if enabled + + // Cast-and-Pack ops are encoded in operation and modifier + assign dst_fmt_is_int = (OpGroup == fpnew_pkg::CONV) & (op_i == fpnew_pkg::F2I); + assign dst_is_cpk = (OpGroup == fpnew_pkg::CONV) & (op_i == fpnew_pkg::CPKAB || + op_i == fpnew_pkg::CPKCD); + assign dst_vec_op = (OpGroup == fpnew_pkg::CONV) & {(op_i == fpnew_pkg::CPKCD), op_mod_i}; + + assign is_up_cast = (fpnew_pkg::fp_width(dst_fmt_i) > fpnew_pkg::fp_width(src_fmt_i)); + assign is_down_cast = (fpnew_pkg::fp_width(dst_fmt_i) < fpnew_pkg::fp_width(src_fmt_i)); + + // The destination format is the int format for F2I casts + assign dst_fmt = dst_fmt_is_int ? int_fmt_i : dst_fmt_i; + + // The data sent along consists of the vectorial flag and format bits + assign aux_data = {dst_fmt_is_int, vectorial_op, dst_fmt}; + assign target_aux_d = {dst_vec_op, dst_is_cpk}; + + // CONV passes one operand for assembly after the unit: opC for cpk, opB for others + if (OpGroup == fpnew_pkg::CONV) begin : conv_target + assign conv_target_d = dst_is_cpk ? operands_i[2] : operands_i[1]; + end + + // For 2-operand units, prepare boxing info + logic [NUM_FORMATS-1:0] is_boxed_1op; + logic [NUM_FORMATS-1:0][1:0] is_boxed_2op; + + always_comb begin : boxed_2op + for (int fmt = 0; fmt < NUM_FORMATS; fmt++) begin + is_boxed_1op[fmt] = is_boxed_i[fmt][0]; + is_boxed_2op[fmt] = is_boxed_i[fmt][1:0]; + end + end + + // --------------- + // Generate Lanes + // --------------- + for (genvar lane = 0; lane < int'(NUM_LANES); lane++) begin : gen_num_lanes + localparam int unsigned LANE = unsigned'(lane); // unsigned to please the linter + // Get a mask of active formats for this lane + localparam fpnew_pkg::fmt_logic_t ACTIVE_FORMATS = + fpnew_pkg::get_lane_formats(Width, FpFmtConfig, LANE); + localparam fpnew_pkg::ifmt_logic_t ACTIVE_INT_FORMATS = + fpnew_pkg::get_lane_int_formats(Width, FpFmtConfig, IntFmtConfig, LANE); + localparam int unsigned MAX_WIDTH = fpnew_pkg::max_fp_width(ACTIVE_FORMATS); + + // Cast-specific parameters + localparam fpnew_pkg::fmt_logic_t CONV_FORMATS = + fpnew_pkg::get_conv_lane_formats(Width, FpFmtConfig, LANE); + localparam fpnew_pkg::ifmt_logic_t CONV_INT_FORMATS = + fpnew_pkg::get_conv_lane_int_formats(Width, FpFmtConfig, IntFmtConfig, LANE); + localparam int unsigned CONV_WIDTH = fpnew_pkg::max_fp_width(CONV_FORMATS); + + // Lane parameters from Opgroup + localparam fpnew_pkg::fmt_logic_t LANE_FORMATS = (OpGroup == fpnew_pkg::CONV) + ? CONV_FORMATS : ACTIVE_FORMATS; + localparam int unsigned LANE_WIDTH = (OpGroup == fpnew_pkg::CONV) ? CONV_WIDTH : MAX_WIDTH; + + logic [LANE_WIDTH-1:0] local_result; // lane-local results + + // Generate instances only if needed, lane 0 always generated + if ((lane == 0) || EnableVectors) begin : active_lane + logic in_valid, out_valid, out_ready; // lane-local handshake + + logic [NUM_OPERANDS-1:0][LANE_WIDTH-1:0] local_operands; // lane-local oprands + logic [LANE_WIDTH-1:0] op_result; // lane-local results + fpnew_pkg::status_t op_status; + + assign in_valid = in_valid_i & ((lane == 0) | vectorial_op); // upper lanes only for vectors + + // Slice out the operands for this lane, upper bits are ignored in the unit + always_comb begin : prepare_input + for (int unsigned i = 0; i < NUM_OPERANDS; i++) begin + local_operands[i] = operands_i[i] >> LANE*fpnew_pkg::fp_width(src_fmt_i); + end + + // override operand 0 for some conversions + if (OpGroup == fpnew_pkg::CONV) begin + // Source is an integer + if (op_i == fpnew_pkg::I2F) begin + local_operands[0] = operands_i[0] >> LANE*fpnew_pkg::int_width(int_fmt_i); + // vectorial F2F up casts + end else if (op_i == fpnew_pkg::F2F) begin + if (vectorial_op && op_mod_i && is_up_cast) begin // up cast with upper half + local_operands[0] = operands_i[0] >> LANE*fpnew_pkg::fp_width(src_fmt_i) + + MAX_FP_WIDTH/2; + end + // CPK + end else if (dst_is_cpk) begin + if (lane == 1) begin + local_operands[0] = operands_i[1][LANE_WIDTH-1:0]; // using opB as second argument + end + end + end + end + + // Instantiate the operation from the selected opgroup + if (OpGroup == fpnew_pkg::ADDMUL) begin : lane_instance + fpnew_fma_multi #( + .FpFmtConfig ( LANE_FORMATS ), + .NumPipeRegs ( NumPipeRegs ), + .PipeConfig ( PipeConfig ), + .TagType ( TagType ), + .AuxType ( logic [AUX_BITS-1:0] ) + ) i_fpnew_fma_multi ( + .clk_i, + .rst_ni, + .operands_i ( local_operands ), + .is_boxed_i, + .rnd_mode_i, + .op_i, + .op_mod_i, + .src_fmt_i, + .dst_fmt_i, + .tag_i, + .aux_i ( aux_data ), + .in_valid_i ( in_valid ), + .in_ready_o ( lane_in_ready[lane] ), + .flush_i, + .result_o ( op_result ), + .status_o ( op_status ), + .extension_bit_o ( lane_ext_bit[lane] ), + .tag_o ( lane_tags[lane] ), + .aux_o ( lane_aux[lane] ), + .out_valid_o ( out_valid ), + .out_ready_i ( out_ready ), + .busy_o ( lane_busy[lane] ) + ); + + end else if (OpGroup == fpnew_pkg::DIVSQRT) begin : lane_instance + fpnew_divsqrt_multi #( + .FpFmtConfig ( LANE_FORMATS ), + .NumPipeRegs ( NumPipeRegs ), + .PipeConfig ( PipeConfig ), + .TagType ( TagType ), + .AuxType ( logic [AUX_BITS-1:0] ) + ) i_fpnew_divsqrt_multi ( + .clk_i, + .rst_ni, + .operands_i ( local_operands[1:0] ), // 2 operands + .is_boxed_i ( is_boxed_2op ), // 2 operands + .rnd_mode_i, + .op_i, + .dst_fmt_i, + .tag_i, + .aux_i ( aux_data ), + .in_valid_i ( in_valid ), + .in_ready_o ( lane_in_ready[lane] ), + .flush_i, + .result_o ( op_result ), + .status_o ( op_status ), + .extension_bit_o ( lane_ext_bit[lane] ), + .tag_o ( lane_tags[lane] ), + .aux_o ( lane_aux[lane] ), + .out_valid_o ( out_valid ), + .out_ready_i ( out_ready ), + .busy_o ( lane_busy[lane] ) + ); + end else if (OpGroup == fpnew_pkg::NONCOMP) begin : lane_instance + + end else if (OpGroup == fpnew_pkg::CONV) begin : lane_instance + fpnew_cast_multi #( + .FpFmtConfig ( LANE_FORMATS ), + .IntFmtConfig ( CONV_INT_FORMATS ), + .NumPipeRegs ( NumPipeRegs ), + .PipeConfig ( PipeConfig ), + .TagType ( TagType ), + .AuxType ( logic [AUX_BITS-1:0] ) + ) i_fpnew_cast_multi ( + .clk_i, + .rst_ni, + .operands_i ( local_operands[0] ), + .is_boxed_i ( is_boxed_1op ), + .rnd_mode_i, + .op_i, + .op_mod_i, + .src_fmt_i, + .dst_fmt_i, + .int_fmt_i, + .tag_i, + .aux_i ( aux_data ), + .in_valid_i ( in_valid ), + .in_ready_o ( lane_in_ready[lane] ), + .flush_i, + .result_o ( op_result ), + .status_o ( op_status ), + .extension_bit_o ( lane_ext_bit[lane] ), + .tag_o ( lane_tags[lane] ), + .aux_o ( lane_aux[lane] ), + .out_valid_o ( out_valid ), + .out_ready_i ( out_ready ), + .busy_o ( lane_busy[lane] ) + ); + end // ADD OTHER OPTIONS HERE + + // Handshakes are only done if the lane is actually used + assign out_ready = out_ready_i & ((lane == 0) | result_is_vector); + assign lane_out_valid[lane] = out_valid & ((lane == 0) | result_is_vector); + + // Properly NaN-box or sign-extend the slice result if not in use + assign local_result = lane_out_valid[lane] ? op_result : '{default: lane_ext_bit[0]}; + assign lane_status[lane] = lane_out_valid[lane] ? op_status : '0; + + // Otherwise generate constant sign-extension + end else begin : inactive_lane + assign lane_out_valid[lane] = 1'b0; // unused lane + assign lane_in_ready[lane] = 1'b0; // unused lane + assign local_result = '{default: lane_ext_bit[0]}; // sign-extend/nan box + assign lane_status[lane] = '0; + assign lane_busy[lane] = 1'b0; + end + + // Generate result packing depending on float format + for (genvar fmt = 0; fmt < NUM_FORMATS; fmt++) begin : pack_fp_result + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + // only for active formats within the lane + if (ACTIVE_FORMATS[fmt]) begin + assign fmt_slice_result[fmt][(LANE+1)*FP_WIDTH-1:LANE*FP_WIDTH] = + local_result[FP_WIDTH-1:0]; + end else if ((LANE+1)*FP_WIDTH <= Width) begin + assign fmt_slice_result[fmt][(LANE+1)*FP_WIDTH-1:LANE*FP_WIDTH] = + '{default: lane_ext_bit[LANE]}; + end else if (LANE*FP_WIDTH < Width) begin + assign fmt_slice_result[fmt][Width-1:LANE*FP_WIDTH] = + '{default: lane_ext_bit[LANE]}; + end + end + + // Generate result packing depending on integer format + if (OpGroup == fpnew_pkg::CONV) begin : int_results_enabled + for (genvar ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++) begin : pack_int_result + // Set up some constants + localparam int unsigned INT_WIDTH = fpnew_pkg::int_width(fpnew_pkg::int_format_e'(ifmt)); + if (ACTIVE_INT_FORMATS[ifmt]) begin + assign ifmt_slice_result[ifmt][(LANE+1)*INT_WIDTH-1:LANE*INT_WIDTH] = + local_result[INT_WIDTH-1:0]; + end else if ((LANE+1)*INT_WIDTH <= Width) begin + assign ifmt_slice_result[ifmt][(LANE+1)*INT_WIDTH-1:LANE*INT_WIDTH] = '0; + end else if (LANE*INT_WIDTH < Width) begin + assign ifmt_slice_result[ifmt][Width-1:LANE*INT_WIDTH] = '0; + end + end + end + end + + // Extend slice result if needed + for (genvar fmt = 0; fmt < NUM_FORMATS; fmt++) begin : extend_fp_result + // Set up some constants + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + if (NUM_LANES*FP_WIDTH < Width) + assign fmt_slice_result[fmt][Width-1:NUM_LANES*FP_WIDTH] = '{default: lane_ext_bit[0]}; + end + + // Mute int results if unused + for (genvar ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++) begin : int_results_disabled + if (OpGroup != fpnew_pkg::CONV) begin : mute_int_result + assign ifmt_slice_result[ifmt] = '0; + end + end + + // Bypass lanes with target operand for vectorial casts + if (OpGroup == fpnew_pkg::CONV) begin : target_regs + // Bypass pipeline signals, index i holds signal after i register stages + logic [0:NumPipeRegs][Width-1:0] byp_pipe_target_q; + logic [0:NumPipeRegs][2:0] byp_pipe_aux_q; + logic [0:NumPipeRegs] byp_pipe_valid_q; + // Ready signal is combinatorial for all stages + logic [0:NumPipeRegs] byp_pipe_ready; + + // Input stage: First element of pipeline is taken from inputs + assign byp_pipe_target_q[0] = conv_target_d; + assign byp_pipe_aux_q[0] = target_aux_d; + assign byp_pipe_valid_q[0] = in_valid_i & vectorial_op; + // Generate the register stages + for (genvar i = 0; i < NumPipeRegs; i++) begin : gen_bypass_pipeline + // Internal register enable for this stage + logic reg_ena; + // Determine the ready signal of the current stage - advance the pipeline: + // 1. if the next stage is ready for our data + // 2. if the next stage only holds a bubble (not valid) -> we can pop it + assign byp_pipe_ready[i] = byp_pipe_ready[i+1] | ~byp_pipe_valid_q[i+1]; + // Valid: enabled by ready signal, synchronous clear with the flush signal + `FFLARNC(byp_pipe_valid_q[i+1], byp_pipe_valid_q[i], byp_pipe_ready[i], flush_i, 1'b0, clk_i, rst_ni) + // Enable register if pipleine ready and a valid data item is present + assign reg_ena = byp_pipe_ready[i] & byp_pipe_valid_q[i]; + // Generate the pipeline registers within the stages, use enable-registers + `FFL(byp_pipe_target_q[i+1], byp_pipe_target_q[i], reg_ena, '0) + `FFL(byp_pipe_aux_q[i+1], byp_pipe_aux_q[i], reg_ena, '0) + end + // Output stage: Ready travels backwards from output side, driven by downstream circuitry + assign byp_pipe_ready[NumPipeRegs] = out_ready_i & result_is_vector; + // Output stage: assign module outputs + assign conv_target_q = byp_pipe_target_q[NumPipeRegs]; + + // decode the aux data + assign {result_vec_op, result_is_cpk} = byp_pipe_aux_q[NumPipeRegs]; + end else begin : no_conv + assign {result_vec_op, result_is_cpk} = '0; + end + + // ------------ + // Output Side + // ------------ + assign {result_fmt_is_int, result_is_vector, result_fmt} = lane_aux[0]; + + assign result_o = result_fmt_is_int + ? ifmt_slice_result[result_fmt] + : fmt_slice_result[result_fmt]; + + assign extension_bit_o = lane_ext_bit[0]; // don't care about upper ones + assign tag_o = lane_tags[0]; // don't care about upper ones + assign busy_o = (| lane_busy); + + assign out_valid_o = lane_out_valid[0]; // don't care about upper ones + + // Collapse the status + always_comb begin : output_processing + // Collapse the status + automatic fpnew_pkg::status_t temp_status; + temp_status = '0; + for (int i = 0; i < int'(NUM_LANES); i++) + temp_status |= lane_status[i]; + status_o = temp_status; + end +endmodule
diff --git a/verilog/rtl/fpnew_pkg.sv b/verilog/rtl/fpnew_pkg.sv new file mode 100644 index 0000000..2d258cf --- /dev/null +++ b/verilog/rtl/fpnew_pkg.sv
@@ -0,0 +1,491 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +package fpnew_pkg; + + // --------- + // FP TYPES + // --------- + // | Enumerator | Format | Width | EXP_BITS | MAN_BITS + // |:----------:|------------------|-------:|:--------:|:--------: + // | FP32 | IEEE binary32 | 32 bit | 8 | 23 + // | FP64 | IEEE binary64 | 64 bit | 11 | 52 + // | FP16 | IEEE binary16 | 16 bit | 5 | 10 + // | FP8 | binary8 | 8 bit | 5 | 2 + // | FP16ALT | binary16alt | 16 bit | 8 | 7 + // *NOTE:* Add new formats only at the end of the enumeration for backwards compatibilty! + + // Encoding for a format + typedef struct packed { + int unsigned exp_bits; + int unsigned man_bits; + } fp_encoding_t; + + localparam int unsigned NUM_FP_FORMATS = 5; // change me to add formats + localparam int unsigned FP_FORMAT_BITS = $clog2(NUM_FP_FORMATS); + + // FP formats + typedef enum logic [FP_FORMAT_BITS-1:0] { + FP32 = 'd0, + FP64 = 'd1, + FP16 = 'd2, + FP8 = 'd3, + FP16ALT = 'd4 + // add new formats here + } fp_format_e; + + // Encodings for supported FP formats + localparam fp_encoding_t [0:NUM_FP_FORMATS-1] FP_ENCODINGS = '{ + '{8, 23}, // IEEE binary32 (single) + '{11, 52}, // IEEE binary64 (double) + '{5, 10}, // IEEE binary16 (half) + '{5, 2}, // custom binary8 + '{8, 7} // custom binary16alt + // add new formats here + }; + + typedef logic [0:NUM_FP_FORMATS-1] fmt_logic_t; // Logic indexed by FP format (for masks) + typedef logic [0:NUM_FP_FORMATS-1][31:0] fmt_unsigned_t; // Unsigned indexed by FP format + + localparam fmt_logic_t CPK_FORMATS = 5'b11000; // FP32 and FP64 can provide CPK only + + // --------- + // INT TYPES + // --------- + // | Enumerator | Width | + // |:----------:|-------:| + // | INT8 | 8 bit | + // | INT16 | 16 bit | + // | INT32 | 32 bit | + // | INT64 | 64 bit | + // *NOTE:* Add new formats only at the end of the enumeration for backwards compatibilty! + + localparam int unsigned NUM_INT_FORMATS = 4; // change me to add formats + localparam int unsigned INT_FORMAT_BITS = $clog2(NUM_INT_FORMATS); + + // Int formats + typedef enum logic [INT_FORMAT_BITS-1:0] { + INT8, + INT16, + INT32, + INT64 + // add new formats here + } int_format_e; + + // Returns the width of an INT format by index + function automatic int unsigned int_width(int_format_e ifmt); + unique case (ifmt) + INT8: return 8; + INT16: return 16; + INT32: return 32; + INT64: return 64; + // default: begin + // pragma translate_off + // $fatal(1, "Invalid INT format supplied"); + // pragma translate_on + // just return any integer to avoid any latches + // hopefully this error is caught by simulation + //return INT8; + //end + endcase + endfunction + + typedef logic [0:NUM_INT_FORMATS-1] ifmt_logic_t; // Logic indexed by INT format (for masks) + + // -------------- + // FP OPERATIONS + // -------------- + localparam int unsigned NUM_OPGROUPS = 4; + + // Each FP operation belongs to an operation group + typedef enum logic [1:0] { + ADDMUL, DIVSQRT, NONCOMP, CONV + } opgroup_e; + + localparam int unsigned OP_BITS = 4; + + typedef enum logic [OP_BITS-1:0] { + FMADD, FNMSUB, ADD, MUL, // ADDMUL operation group + DIV, SQRT, // DIVSQRT operation group + SGNJ, MINMAX, CMP, CLASSIFY, // NONCOMP operation group + F2F, F2I, I2F, CPKAB, CPKCD // CONV operation group + } operation_e; + + // ------------------- + // RISC-V FP-SPECIFIC + // ------------------- + // Rounding modes + typedef enum logic [2:0] { + RNE = 3'b000, + RTZ = 3'b001, + RDN = 3'b010, + RUP = 3'b011, + RMM = 3'b100, + DYN = 3'b111 + } roundmode_e; + + // Status flags + typedef struct packed { + logic NV; // Invalid + logic DZ; // Divide by zero + logic OF; // Overflow + logic UF; // Underflow + logic NX; // Inexact + } status_t; + + // Information about a floating point value + typedef struct packed { + logic is_normal; // is the value normal + logic is_subnormal; // is the value subnormal + logic is_zero; // is the value zero + logic is_inf; // is the value infinity + logic is_nan; // is the value NaN + logic is_signalling; // is the value a signalling NaN + logic is_quiet; // is the value a quiet NaN + logic is_boxed; // is the value properly NaN-boxed (RISC-V specific) + } fp_info_t; + + // Classification mask + typedef enum logic [9:0] { + NEGINF = 10'b00_0000_0001, + NEGNORM = 10'b00_0000_0010, + NEGSUBNORM = 10'b00_0000_0100, + NEGZERO = 10'b00_0000_1000, + POSZERO = 10'b00_0001_0000, + POSSUBNORM = 10'b00_0010_0000, + POSNORM = 10'b00_0100_0000, + POSINF = 10'b00_1000_0000, + SNAN = 10'b01_0000_0000, + QNAN = 10'b10_0000_0000 + } classmask_e; + + // ------------------ + // FPU configuration + // ------------------ + // Pipelining registers can be inserted (at elaboration time) into operational units + typedef enum logic [1:0] { + BEFORE, // registers are inserted at the inputs of the unit + AFTER, // registers are inserted at the outputs of the unit + INSIDE, // registers are inserted at predetermined (suboptimal) locations in the unit + DISTRIBUTED // registers are evenly distributed, INSIDE >= AFTER >= BEFORE + } pipe_config_t; + + // Arithmetic units can be arranged in parallel (per format), merged (multi-format) or not at all. + typedef enum logic [1:0] { + DISABLED, // arithmetic units are not generated + PARALLEL, // arithmetic units are generated in prallel slices, one for each format + MERGED // arithmetic units are contained within a merged unit holding multiple formats + } unit_type_t; + + // Array of unit types indexed by format + typedef unit_type_t [0:NUM_FP_FORMATS-1] fmt_unit_types_t; + + // Array of format-specific unit types by opgroup + typedef fmt_unit_types_t [0:NUM_OPGROUPS-1] opgrp_fmt_unit_types_t; + // same with unsigned + typedef fmt_unsigned_t [0:NUM_OPGROUPS-1] opgrp_fmt_unsigned_t; + + // FPU configuration: features + typedef struct packed { + int unsigned Width; + logic EnableVectors; + logic EnableNanBox; + fmt_logic_t FpFmtMask; + ifmt_logic_t IntFmtMask; + } fpu_features_t; + + localparam fpu_features_t RV64D = '{ + Width: 64, + EnableVectors: 1'b0, + EnableNanBox: 1'b1, + FpFmtMask: 5'b11000, + IntFmtMask: 4'b0011 + }; + + localparam fpu_features_t RV32D = '{ + Width: 64, + EnableVectors: 1'b1, + EnableNanBox: 1'b1, + FpFmtMask: 5'b11000, + IntFmtMask: 4'b0010 + }; + + localparam fpu_features_t RV32F = '{ + Width: 32, + EnableVectors: 1'b0, + EnableNanBox: 1'b1, + FpFmtMask: 5'b10000, + IntFmtMask: 4'b0010 + }; + + localparam fpu_features_t RV64D_Xsflt = '{ + Width: 64, + EnableVectors: 1'b1, + EnableNanBox: 1'b1, + FpFmtMask: 5'b11111, + IntFmtMask: 4'b1111 + }; + + localparam fpu_features_t RV32F_Xsflt = '{ + Width: 32, + EnableVectors: 1'b1, + EnableNanBox: 1'b1, + FpFmtMask: 5'b10111, + IntFmtMask: 4'b1110 + }; + + localparam fpu_features_t RV32F_Xf16alt_Xfvec = '{ + Width: 32, + EnableVectors: 1'b1, + EnableNanBox: 1'b1, + FpFmtMask: 5'b10001, + IntFmtMask: 4'b0110 + }; + + // FPU configuraion: implementation + typedef struct packed { + opgrp_fmt_unsigned_t PipeRegs; + opgrp_fmt_unit_types_t UnitTypes; + pipe_config_t PipeConfig; + } fpu_implementation_t; + + localparam fpu_implementation_t DEFAULT_NOREGS = '{ + PipeRegs: '{default: 0}, + UnitTypes: '{'{default: PARALLEL}, // ADDMUL + '{default: MERGED}, // DIVSQRT + '{default: PARALLEL}, // NONCOMP + '{default: MERGED}}, // CONV + PipeConfig: BEFORE + }; + + localparam fpu_implementation_t DEFAULT_SNITCH = '{ + PipeRegs: '{default: 1}, + UnitTypes: '{'{default: PARALLEL}, // ADDMUL + '{default: DISABLED}, // DIVSQRT + '{default: PARALLEL}, // NONCOMP + '{default: MERGED}}, // CONV + PipeConfig: BEFORE + }; + + // ----------------------- + // Synthesis optimization + // ----------------------- + localparam logic DONT_CARE = 1'b1; // the value to assign as don't care + + // ------------------------- + // General helper functions + // ------------------------- + function automatic int minimum(int a, int b); + return (a < b) ? a : b; + endfunction + + function automatic int maximum(int a, int b); + return (a > b) ? a : b; + endfunction + + // ------------------------------------------- + // Helper functions for FP formats and values + // ------------------------------------------- + // Returns the width of a FP format + function automatic int unsigned fp_width(fp_format_e fmt); + return FP_ENCODINGS[fmt].exp_bits + FP_ENCODINGS[fmt].man_bits + 1; + endfunction + + // Returns the widest FP format present + function automatic int unsigned max_fp_width(fmt_logic_t cfg); + automatic int unsigned res = 0; + for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) + if (cfg[i]) + res = unsigned'(maximum(res, fp_width(fp_format_e'(i)))); + return res; + endfunction + + // Returns the narrowest FP format present + function automatic int unsigned min_fp_width(fmt_logic_t cfg); + automatic int unsigned res = max_fp_width(cfg); + for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) + if (cfg[i]) + res = unsigned'(minimum(res, fp_width(fp_format_e'(i)))); + return res; + endfunction + + // Returns the number of expoent bits for a format + function automatic int unsigned exp_bits(fp_format_e fmt); + return FP_ENCODINGS[fmt].exp_bits; + endfunction + + // Returns the number of mantissa bits for a format + function automatic int unsigned man_bits(fp_format_e fmt); + return FP_ENCODINGS[fmt].man_bits; + endfunction + + // Returns the bias value for a given format (as per IEEE 754-2008) + function automatic int unsigned bias(fp_format_e fmt); + return unsigned'(2**(FP_ENCODINGS[fmt].exp_bits-1)-1); // symmetrical bias + endfunction + + function automatic fp_encoding_t super_format(fmt_logic_t cfg); + automatic fp_encoding_t res; + res = '0; + for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++) + if (cfg[fmt]) begin // only active format + res.exp_bits = unsigned'(maximum(res.exp_bits, exp_bits(fp_format_e'(fmt)))); + res.man_bits = unsigned'(maximum(res.man_bits, man_bits(fp_format_e'(fmt)))); + end + return res; + endfunction + + // ------------------------------------------- + // Helper functions for INT formats and values + // ------------------------------------------- + // Returns the widest INT format present + function automatic int unsigned max_int_width(ifmt_logic_t cfg); + automatic int unsigned res = 0; + for (int ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++) begin + if (cfg[ifmt]) res = maximum(res, int_width(int_format_e'(ifmt))); + end + return res; + endfunction + + // -------------------------------------------------- + // Helper functions for operations and FPU structure + // -------------------------------------------------- + // Returns the operation group of the given operation + function automatic opgroup_e get_opgroup(operation_e op); + unique case (op) + FMADD, FNMSUB, ADD, MUL: return ADDMUL; + DIV, SQRT: return DIVSQRT; + SGNJ, MINMAX, CMP, CLASSIFY: return NONCOMP; + F2F, F2I, I2F, CPKAB, CPKCD: return CONV; + default: return NONCOMP; + endcase + endfunction + + // Returns the number of operands by operation group + function automatic int unsigned num_operands(opgroup_e grp); + unique case (grp) + ADDMUL: return 3; + DIVSQRT: return 2; + NONCOMP: return 2; + CONV: return 3; // vectorial casts use 3 operands + default: return 0; + endcase + endfunction + + // Returns the number of lanes according to width, format and vectors + function automatic int unsigned num_lanes(int unsigned width, fp_format_e fmt, logic vec); + return vec ? width / fp_width(fmt) : 1; // if no vectors, only one lane + endfunction + + // Returns the maximum number of lanes in the FPU according to width, format config and vectors + function automatic int unsigned max_num_lanes(int unsigned width, fmt_logic_t cfg, logic vec); + return vec ? width / min_fp_width(cfg) : 1; // if no vectors, only one lane + endfunction + + // Returns a mask of active FP formats that are present in lane lane_no of a multiformat slice + function automatic fmt_logic_t get_lane_formats(int unsigned width, + fmt_logic_t cfg, + int unsigned lane_no); + automatic fmt_logic_t res; + for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++) + // Mask active formats with the number of lanes for that format + res[fmt] = cfg[fmt] & (width / fp_width(fp_format_e'(fmt)) > lane_no); + return res; + endfunction + + // Returns a mask of active INT formats that are present in lane lane_no of a multiformat slice + function automatic ifmt_logic_t get_lane_int_formats(int unsigned width, + fmt_logic_t cfg, + ifmt_logic_t icfg, + int unsigned lane_no); + automatic ifmt_logic_t res; + automatic fmt_logic_t lanefmts; + res = '0; + lanefmts = get_lane_formats(width, cfg, lane_no); + + for (int unsigned ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++) + for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++) + // Mask active int formats with the width of the float formats + if ((fp_width(fp_format_e'(fmt)) == int_width(int_format_e'(ifmt)))) + res[ifmt] |= icfg[ifmt] && lanefmts[fmt]; + return res; + endfunction + + // Returns a mask of active FP formats that are present in lane lane_no of a CONV slice + function automatic fmt_logic_t get_conv_lane_formats(int unsigned width, + fmt_logic_t cfg, + int unsigned lane_no); + automatic fmt_logic_t res; + for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++) + // Mask active formats with the number of lanes for that format, CPK at least twice + res[fmt] = cfg[fmt] && ((width / fp_width(fp_format_e'(fmt)) > lane_no) || + (CPK_FORMATS[fmt] && (lane_no < 2))); + return res; + endfunction + + // Returns a mask of active INT formats that are present in lane lane_no of a CONV slice + function automatic ifmt_logic_t get_conv_lane_int_formats(int unsigned width, + fmt_logic_t cfg, + ifmt_logic_t icfg, + int unsigned lane_no); + automatic ifmt_logic_t res; + automatic fmt_logic_t lanefmts; + res = '0; + lanefmts = get_conv_lane_formats(width, cfg, lane_no); + + for (int unsigned ifmt = 0; ifmt < NUM_INT_FORMATS; ifmt++) + for (int unsigned fmt = 0; fmt < NUM_FP_FORMATS; fmt++) + // Mask active int formats with the width of the float formats + res[ifmt] |= icfg[ifmt] && lanefmts[fmt] && + (fp_width(fp_format_e'(fmt)) == int_width(int_format_e'(ifmt))); + return res; + endfunction + + // Return whether any active format is set as MERGED + function automatic logic any_enabled_multi(fmt_unit_types_t types, fmt_logic_t cfg); + for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) + if (cfg[i] && types[i] == MERGED) + return 1'b1; + return 1'b0; + endfunction + + // Return whether the given format is the first active one set as MERGED + function automatic logic is_first_enabled_multi(fp_format_e fmt, + fmt_unit_types_t types, + fmt_logic_t cfg); + for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) begin + if (cfg[i] && types[i] == MERGED) return (fp_format_e'(i) == fmt); + end + return 1'b0; + endfunction + + // Returns the first format that is active and is set as MERGED + function automatic fp_format_e get_first_enabled_multi(fmt_unit_types_t types, fmt_logic_t cfg); + for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) + if (cfg[i] && types[i] == MERGED) + return fp_format_e'(i); + return fp_format_e'(0); + endfunction + + // Returns the largest number of regs that is active and is set as MERGED + function automatic int unsigned get_num_regs_multi(fmt_unsigned_t regs, + fmt_unit_types_t types, + fmt_logic_t cfg); + automatic int unsigned res = 0; + for (int unsigned i = 0; i < NUM_FP_FORMATS; i++) begin + if (cfg[i] && types[i] == MERGED) res = maximum(res, regs[i]); + end + return res; + endfunction + +endpackage
diff --git a/verilog/rtl/fpnew_rounding.sv b/verilog/rtl/fpnew_rounding.sv new file mode 100644 index 0000000..4e4b7c7 --- /dev/null +++ b/verilog/rtl/fpnew_rounding.sv
@@ -0,0 +1,72 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_rounding #( + parameter int unsigned AbsWidth=2 // Width of the abolute value, without sign bit +) ( + // Input value + input logic [AbsWidth-1:0] abs_value_i, // absolute value without sign + input logic sign_i, + // Rounding information + input logic [1:0] round_sticky_bits_i, // round and sticky bits {RS} + input fpnew_pkg::roundmode_e rnd_mode_i, + input logic effective_subtraction_i, // sign of inputs affects rounding of zeroes + // Output value + output logic [AbsWidth-1:0] abs_rounded_o, // absolute value without sign + output logic sign_o, + // Output classification + output logic exact_zero_o // output is an exact zero +); + + logic round_up; // Rounding decision + + // Take the rounding decision according to RISC-V spec + // RoundMode | Mnemonic | Meaning + // :--------:|:--------:|:------- + // 000 | RNE | Round to Nearest, ties to Even + // 001 | RTZ | Round towards Zero + // 010 | RDN | Round Down (towards -\infty) + // 011 | RUP | Round Up (towards \infty) + // 100 | RMM | Round to Nearest, ties to Max Magnitude + // others | | *invalid* + always_comb begin : rounding_decision + unique case (rnd_mode_i) + fpnew_pkg::RNE: // Decide accoring to round/sticky bits + unique case (round_sticky_bits_i) + 2'b00, + 2'b01: round_up = 1'b0; // < ulp/2 away, round down + 2'b10: round_up = abs_value_i[0]; // = ulp/2 away, round towards even result + 2'b11: round_up = 1'b1; // > ulp/2 away, round up + //default: round_up = fpnew_pkg::DONT_CARE; + endcase + fpnew_pkg::RTZ: round_up = 1'b0; // always round down + fpnew_pkg::RDN: round_up = (| round_sticky_bits_i) ? sign_i : 1'b0; // to 0 if +, away if - + fpnew_pkg::RUP: round_up = (| round_sticky_bits_i) ? ~sign_i : 1'b0; // to 0 if -, away if + + fpnew_pkg::RMM: round_up = round_sticky_bits_i[1]; // round down if < ulp/2 away, else up + default: round_up = fpnew_pkg::DONT_CARE; // propagate x + endcase + end + + // Perform the rounding, exponent change and overflow to inf happens automagically + assign abs_rounded_o = abs_value_i + round_up; + + // True zero result is a zero result without dirty round/sticky bits + assign exact_zero_o = (abs_value_i == '0) && (round_sticky_bits_i == '0); + + // In case of effective subtraction (thus signs of addition operands must have differed) and a + // true zero result, the result sign is '-' in case of RDN and '+' for other modes. + assign sign_o = (exact_zero_o && effective_subtraction_i) + ? (rnd_mode_i == fpnew_pkg::RDN) + : sign_i; + +endmodule
diff --git a/verilog/rtl/fpnew_top.sv b/verilog/rtl/fpnew_top.sv new file mode 100644 index 0000000..5b37edd --- /dev/null +++ b/verilog/rtl/fpnew_top.sv
@@ -0,0 +1,172 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// Author: Stefan Mach <smach@iis.ee.ethz.ch> + +module fpnew_top #( + // FPU configuration + parameter fpnew_pkg::fpu_features_t Features = fpnew_pkg::RV64D_Xsflt, + parameter fpnew_pkg::fpu_implementation_t Implementation = fpnew_pkg::DEFAULT_NOREGS, + parameter type TagType = logic, + // Do not change + localparam int unsigned WIDTH = Features.Width, + localparam int unsigned NUM_OPERANDS = 3 +) ( + input logic clk_i, + input logic rst_ni, + // Input signals + input logic [NUM_OPERANDS-1:0][WIDTH-1:0] operands_i, + input fpnew_pkg::roundmode_e rnd_mode_i, + input fpnew_pkg::operation_e op_i, + input logic op_mod_i, + input fpnew_pkg::fp_format_e src_fmt_i, + input fpnew_pkg::fp_format_e dst_fmt_i, + input fpnew_pkg::int_format_e int_fmt_i, + input logic vectorial_op_i, + input TagType tag_i, + // Input Handshake + input logic in_valid_i, + output logic in_ready_o, + input logic flush_i, + // Output signals + output logic [WIDTH-1:0] result_o, + output fpnew_pkg::status_t status_o, + output TagType tag_o, + // Output handshake + output logic out_valid_o, + input logic out_ready_i, + // Indication of valid data in flight + output logic busy_o +); + + localparam int unsigned NUM_OPGROUPS = fpnew_pkg::NUM_OPGROUPS; + localparam int unsigned NUM_FORMATS = fpnew_pkg::NUM_FP_FORMATS; + + // ---------------- + // Type Definition + // ---------------- + typedef struct packed { + logic [WIDTH-1:0] result; + fpnew_pkg::status_t status; + TagType tag; + } output_t; + + // Handshake signals for the blocks + logic [NUM_OPGROUPS-1:0] opgrp_in_ready, opgrp_out_valid, opgrp_out_ready, opgrp_ext, opgrp_busy; + output_t [NUM_OPGROUPS-1:0] opgrp_outputs; + + logic [NUM_FORMATS-1:0][NUM_OPERANDS-1:0] is_boxed; + + // ----------- + // Input Side + // ----------- + assign in_ready_o = in_valid_i & opgrp_in_ready[fpnew_pkg::get_opgroup(op_i)]; + + // NaN-boxing check + for (genvar fmt = 0; fmt < int'(NUM_FORMATS); fmt++) begin : gen_nanbox_check + localparam int unsigned FP_WIDTH = fpnew_pkg::fp_width(fpnew_pkg::fp_format_e'(fmt)); + // NaN boxing is only generated if it's enabled and needed + if (Features.EnableNanBox && (FP_WIDTH < WIDTH)) begin : check + for (genvar op = 0; op < int'(NUM_OPERANDS); op++) begin : operands + assign is_boxed[fmt][op] = (!vectorial_op_i) + ? operands_i[op][WIDTH-1:FP_WIDTH] == '1 + : 1'b1; + end + end else begin : no_check + assign is_boxed[fmt] = '1; + end + end + + // ------------------------- + // Generate Operation Blocks + // ------------------------- + for (genvar opgrp = 0; opgrp < int'(NUM_OPGROUPS); opgrp++) begin : gen_operation_groups + localparam int unsigned NUM_OPS = fpnew_pkg::num_operands(fpnew_pkg::opgroup_e'(opgrp)); + + logic in_valid; + logic [NUM_FORMATS-1:0][NUM_OPS-1:0] input_boxed; + + assign in_valid = in_valid_i & (fpnew_pkg::get_opgroup(op_i) == fpnew_pkg::opgroup_e'(opgrp)); + + // slice out input boxing + always_comb begin : slice_inputs + for (int unsigned fmt = 0; fmt < NUM_FORMATS; fmt++) + input_boxed[fmt] = is_boxed[fmt][NUM_OPS-1:0]; + end + + fpnew_opgroup_block #( + .OpGroup ( fpnew_pkg::opgroup_e'(opgrp) ), + .Width ( WIDTH ), + .EnableVectors ( Features.EnableVectors ), + .FpFmtMask ( Features.FpFmtMask ), + .IntFmtMask ( Features.IntFmtMask ), + .FmtPipeRegs ( Implementation.PipeRegs[opgrp] ), + .FmtUnitTypes ( Implementation.UnitTypes[opgrp] ), + .PipeConfig ( Implementation.PipeConfig ), + .TagType ( TagType ) + ) i_opgroup_block ( + .clk_i, + .rst_ni, + .operands_i ( operands_i[NUM_OPS-1:0] ), + .is_boxed_i ( input_boxed ), + .rnd_mode_i, + .op_i, + .op_mod_i, + .src_fmt_i, + .dst_fmt_i, + .int_fmt_i, + .vectorial_op_i, + .tag_i, + .in_valid_i ( in_valid ), + .in_ready_o ( opgrp_in_ready[opgrp] ), + .flush_i, + .result_o ( opgrp_outputs[opgrp].result ), + .status_o ( opgrp_outputs[opgrp].status ), + .extension_bit_o ( opgrp_ext[opgrp] ), + .tag_o ( opgrp_outputs[opgrp].tag ), + .out_valid_o ( opgrp_out_valid[opgrp] ), + .out_ready_i ( opgrp_out_ready[opgrp] ), + .busy_o ( opgrp_busy[opgrp] ) + ); + end + + // ------------------ + // Arbitrate Outputs + // ------------------ + output_t arbiter_output; + + // Round-Robin arbiter to decide which result to use + rr_arb_tree #( + .NumIn ( NUM_OPGROUPS ), + .DataType ( output_t ), + .AxiVldRdy ( 1'b1 ) + ) i_arbiter ( + .clk_i, + .rst_ni, + .flush_i, + .rr_i ( '0 ), + .req_i ( opgrp_out_valid ), + .gnt_o ( opgrp_out_ready ), + .data_i ( opgrp_outputs ), + .gnt_i ( out_ready_i ), + .req_o ( out_valid_o ), + .data_o ( arbiter_output ), + .idx_o ( /* unused */ ) + ); + + // Unpack output + assign result_o = arbiter_output.result; + assign status_o = arbiter_output.status; + assign tag_o = arbiter_output.tag; + + assign busy_o = (| opgrp_busy); + +endmodule
diff --git a/verilog/rtl/gpio.sv b/verilog/rtl/gpio.sv new file mode 100644 index 0000000..762553c --- /dev/null +++ b/verilog/rtl/gpio.sv
@@ -0,0 +1,144 @@ + +// General Purpose Input/Output module + + +module gpio ( + input clk_i, + input rst_ni, + + // Below Regster interface can be changed + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + input [31:0] cio_gpio_i, + output logic [31:0] cio_gpio_o, + output logic [31:0] cio_gpio_en_o, + + output logic [31:0] intr_gpio_o +); + + import gpio_reg_pkg::* ; + + gpio_reg2hw_t reg2hw; + gpio_hw2reg_t hw2reg; + + logic [31:0] cio_gpio_q; + logic [31:0] cio_gpio_en_q; + + // possibly filter the input based upon register configuration + + logic [31:0] data_in_d; + + for (genvar i = 0 ; i < 32 ; i++) begin : gen_filter + prim_filter_ctr #(.Cycles(16)) filter ( + .clk_i, + .rst_ni, + .enable_i(reg2hw.ctrl_en_input_filter.q[i]), + .filter_i(cio_gpio_i[i]), + .filter_o(data_in_d[i]) + ); + end + + // GPIO_IN + assign hw2reg.data_in.de = 1'b1; + assign hw2reg.data_in.d = data_in_d; + + // GPIO_OUT + assign cio_gpio_o = cio_gpio_q; + assign cio_gpio_en_o = cio_gpio_en_q; + + assign hw2reg.direct_out.d = cio_gpio_q; + assign hw2reg.masked_out_upper.data.d = cio_gpio_q[31:16]; + assign hw2reg.masked_out_upper.mask.d = 16'h 0; + assign hw2reg.masked_out_lower.data.d = cio_gpio_q[15:0]; + assign hw2reg.masked_out_lower.mask.d = 16'h 0; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cio_gpio_q <= '0; + end else if (reg2hw.direct_out.qe) begin + cio_gpio_q <= reg2hw.direct_out.q; + end else if (reg2hw.masked_out_upper.data.qe) begin + cio_gpio_q[31:16] <= + ( reg2hw.masked_out_upper.mask.q & reg2hw.masked_out_upper.data.q) | + (~reg2hw.masked_out_upper.mask.q & cio_gpio_q[31:16]); + end else if (reg2hw.masked_out_lower.data.qe) begin + cio_gpio_q[15:0] <= + ( reg2hw.masked_out_lower.mask.q & reg2hw.masked_out_lower.data.q) | + (~reg2hw.masked_out_lower.mask.q & cio_gpio_q[15:0]); + end + end + + // GPIO OE + assign hw2reg.direct_oe.d = cio_gpio_en_q; + assign hw2reg.masked_oe_upper.data.d = cio_gpio_en_q[31:16]; + assign hw2reg.masked_oe_upper.mask.d = 16'h 0; + assign hw2reg.masked_oe_lower.data.d = cio_gpio_en_q[15:0]; + assign hw2reg.masked_oe_lower.mask.d = 16'h 0; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + cio_gpio_en_q <= '0; + end else if (reg2hw.direct_oe.qe) begin + cio_gpio_en_q <= reg2hw.direct_oe.q; + end else if (reg2hw.masked_oe_upper.data.qe) begin + cio_gpio_en_q[31:16] <= + ( reg2hw.masked_oe_upper.mask.q & reg2hw.masked_oe_upper.data.q) | + (~reg2hw.masked_oe_upper.mask.q & cio_gpio_en_q[31:16]); + end else if (reg2hw.masked_oe_lower.data.qe) begin + cio_gpio_en_q[15:0] <= + ( reg2hw.masked_oe_lower.mask.q & reg2hw.masked_oe_lower.data.q) | + (~reg2hw.masked_oe_lower.mask.q & cio_gpio_en_q[15:0]); + end + end + + logic [31:0] data_in_q; + always_ff @(posedge clk_i) begin + data_in_q <= data_in_d; + end + + logic [31:0] event_intr_rise, event_intr_fall, event_intr_actlow, event_intr_acthigh; + logic [31:0] event_intr_combined; + + // instantiate interrupt hardware primitive + prim_intr_hw #(.Width(32)) intr_hw ( + .clk_i, + .rst_ni, + .event_intr_i (event_intr_combined), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.d), + .intr_o (intr_gpio_o) + ); + + // detect four possible individual interrupts + assign event_intr_rise = (~data_in_q & data_in_d) & reg2hw.intr_ctrl_en_rising.q; + assign event_intr_fall = ( data_in_q & ~data_in_d) & reg2hw.intr_ctrl_en_falling.q; + assign event_intr_acthigh = data_in_d & reg2hw.intr_ctrl_en_lvlhigh.q; + assign event_intr_actlow = ~data_in_d & reg2hw.intr_ctrl_en_lvllow.q; + + assign event_intr_combined = event_intr_rise | + event_intr_fall | + event_intr_actlow | + event_intr_acthigh; + + + // Register module + gpio_reg_top u_reg ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + .tl_i (tl_i), + .tl_o (tl_o), + + .reg2hw (reg2hw), + .hw2reg (hw2reg), + + .devmode_i (1'b1) + ); + + +endmodule
diff --git a/verilog/rtl/gpio_reg_pkg.sv b/verilog/rtl/gpio_reg_pkg.sv new file mode 100644 index 0000000..b85347a --- /dev/null +++ b/verilog/rtl/gpio_reg_pkg.sv
@@ -0,0 +1,248 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package gpio_reg_pkg; + + // Address width within the block + parameter int BlockAw = 6; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_intr_state_reg_t; + + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_intr_enable_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } gpio_reg2hw_intr_test_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } gpio_reg2hw_direct_out_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] q; + logic qe; + } data; + struct packed { + logic [15:0] q; + logic qe; + } mask; + } gpio_reg2hw_masked_out_lower_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] q; + logic qe; + } data; + struct packed { + logic [15:0] q; + logic qe; + } mask; + } gpio_reg2hw_masked_out_upper_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } gpio_reg2hw_direct_oe_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] q; + logic qe; + } data; + struct packed { + logic [15:0] q; + logic qe; + } mask; + } gpio_reg2hw_masked_oe_lower_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] q; + logic qe; + } data; + struct packed { + logic [15:0] q; + logic qe; + } mask; + } gpio_reg2hw_masked_oe_upper_reg_t; + + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_intr_ctrl_en_rising_reg_t; + + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_intr_ctrl_en_falling_reg_t; + + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_intr_ctrl_en_lvlhigh_reg_t; + + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_intr_ctrl_en_lvllow_reg_t; + + typedef struct packed { + logic [31:0] q; + } gpio_reg2hw_ctrl_en_input_filter_reg_t; + + + typedef struct packed { + logic [31:0] d; + logic de; + } gpio_hw2reg_intr_state_reg_t; + + typedef struct packed { + logic [31:0] d; + logic de; + } gpio_hw2reg_data_in_reg_t; + + typedef struct packed { + logic [31:0] d; + } gpio_hw2reg_direct_out_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] d; + } data; + struct packed { + logic [15:0] d; + } mask; + } gpio_hw2reg_masked_out_lower_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] d; + } data; + struct packed { + logic [15:0] d; + } mask; + } gpio_hw2reg_masked_out_upper_reg_t; + + typedef struct packed { + logic [31:0] d; + } gpio_hw2reg_direct_oe_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] d; + } data; + struct packed { + logic [15:0] d; + } mask; + } gpio_hw2reg_masked_oe_lower_reg_t; + + typedef struct packed { + struct packed { + logic [15:0] d; + } data; + struct packed { + logic [15:0] d; + } mask; + } gpio_hw2reg_masked_oe_upper_reg_t; + + + /////////////////////////////////////// + // Register to internal design logic // + /////////////////////////////////////// + typedef struct packed { + gpio_reg2hw_intr_state_reg_t intr_state; // [458:427] + gpio_reg2hw_intr_enable_reg_t intr_enable; // [426:395] + gpio_reg2hw_intr_test_reg_t intr_test; // [394:362] + gpio_reg2hw_direct_out_reg_t direct_out; // [361:329] + gpio_reg2hw_masked_out_lower_reg_t masked_out_lower; // [328:295] + gpio_reg2hw_masked_out_upper_reg_t masked_out_upper; // [294:261] + gpio_reg2hw_direct_oe_reg_t direct_oe; // [260:228] + gpio_reg2hw_masked_oe_lower_reg_t masked_oe_lower; // [227:194] + gpio_reg2hw_masked_oe_upper_reg_t masked_oe_upper; // [193:160] + gpio_reg2hw_intr_ctrl_en_rising_reg_t intr_ctrl_en_rising; // [159:128] + gpio_reg2hw_intr_ctrl_en_falling_reg_t intr_ctrl_en_falling; // [127:96] + gpio_reg2hw_intr_ctrl_en_lvlhigh_reg_t intr_ctrl_en_lvlhigh; // [95:64] + gpio_reg2hw_intr_ctrl_en_lvllow_reg_t intr_ctrl_en_lvllow; // [63:32] + gpio_reg2hw_ctrl_en_input_filter_reg_t ctrl_en_input_filter; // [31:0] + } gpio_reg2hw_t; + + /////////////////////////////////////// + // Internal design logic to register // + /////////////////////////////////////// + typedef struct packed { + gpio_hw2reg_intr_state_reg_t intr_state; // [257:225] + gpio_hw2reg_data_in_reg_t data_in; // [224:192] + gpio_hw2reg_direct_out_reg_t direct_out; // [191:160] + gpio_hw2reg_masked_out_lower_reg_t masked_out_lower; // [159:128] + gpio_hw2reg_masked_out_upper_reg_t masked_out_upper; // [127:96] + gpio_hw2reg_direct_oe_reg_t direct_oe; // [95:64] + gpio_hw2reg_masked_oe_lower_reg_t masked_oe_lower; // [63:32] + gpio_hw2reg_masked_oe_upper_reg_t masked_oe_upper; // [31:0] + } gpio_hw2reg_t; + + // Register Address + parameter logic [BlockAw-1:0] GPIO_INTR_STATE_OFFSET = 6'h 0; + parameter logic [BlockAw-1:0] GPIO_INTR_ENABLE_OFFSET = 6'h 4; + parameter logic [BlockAw-1:0] GPIO_INTR_TEST_OFFSET = 6'h 8; + parameter logic [BlockAw-1:0] GPIO_DATA_IN_OFFSET = 6'h c; + parameter logic [BlockAw-1:0] GPIO_DIRECT_OUT_OFFSET = 6'h 10; + parameter logic [BlockAw-1:0] GPIO_MASKED_OUT_LOWER_OFFSET = 6'h 14; + parameter logic [BlockAw-1:0] GPIO_MASKED_OUT_UPPER_OFFSET = 6'h 18; + parameter logic [BlockAw-1:0] GPIO_DIRECT_OE_OFFSET = 6'h 1c; + parameter logic [BlockAw-1:0] GPIO_MASKED_OE_LOWER_OFFSET = 6'h 20; + parameter logic [BlockAw-1:0] GPIO_MASKED_OE_UPPER_OFFSET = 6'h 24; + parameter logic [BlockAw-1:0] GPIO_INTR_CTRL_EN_RISING_OFFSET = 6'h 28; + parameter logic [BlockAw-1:0] GPIO_INTR_CTRL_EN_FALLING_OFFSET = 6'h 2c; + parameter logic [BlockAw-1:0] GPIO_INTR_CTRL_EN_LVLHIGH_OFFSET = 6'h 30; + parameter logic [BlockAw-1:0] GPIO_INTR_CTRL_EN_LVLLOW_OFFSET = 6'h 34; + parameter logic [BlockAw-1:0] GPIO_CTRL_EN_INPUT_FILTER_OFFSET = 6'h 38; + + + // Register Index + typedef enum int { + GPIO_INTR_STATE, + GPIO_INTR_ENABLE, + GPIO_INTR_TEST, + GPIO_DATA_IN, + GPIO_DIRECT_OUT, + GPIO_MASKED_OUT_LOWER, + GPIO_MASKED_OUT_UPPER, + GPIO_DIRECT_OE, + GPIO_MASKED_OE_LOWER, + GPIO_MASKED_OE_UPPER, + GPIO_INTR_CTRL_EN_RISING, + GPIO_INTR_CTRL_EN_FALLING, + GPIO_INTR_CTRL_EN_LVLHIGH, + GPIO_INTR_CTRL_EN_LVLLOW, + GPIO_CTRL_EN_INPUT_FILTER + } gpio_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] GPIO_PERMIT [15] = '{ + 4'b 1111, // index[ 0] GPIO_INTR_STATE + 4'b 1111, // index[ 1] GPIO_INTR_ENABLE + 4'b 1111, // index[ 2] GPIO_INTR_TEST + 4'b 1111, // index[ 3] GPIO_DATA_IN + 4'b 1111, // index[ 4] GPIO_DIRECT_OUT + 4'b 1111, // index[ 5] GPIO_MASKED_OUT_LOWER + 4'b 1111, // index[ 6] GPIO_MASKED_OUT_UPPER + 4'b 1111, // index[ 7] GPIO_DIRECT_OE + 4'b 1111, // index[ 8] GPIO_MASKED_OE_LOWER + 4'b 1111, // index[ 9] GPIO_MASKED_OE_UPPER + 4'b 1111, // index[10] GPIO_INTR_CTRL_EN_RISING + 4'b 1111, // index[11] GPIO_INTR_CTRL_EN_FALLING + 4'b 1111, // index[12] GPIO_INTR_CTRL_EN_LVLHIGH + 4'b 1111, // index[13] GPIO_INTR_CTRL_EN_LVLLOW + 4'b 1111 // index[14] GPIO_CTRL_EN_INPUT_FILTER + }; +endpackage +
diff --git a/verilog/rtl/gpio_reg_top.sv b/verilog/rtl/gpio_reg_top.sv new file mode 100644 index 0000000..1c2ba13 --- /dev/null +++ b/verilog/rtl/gpio_reg_top.sv
@@ -0,0 +1,706 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +module gpio_reg_top ( + input clk_i, + input rst_ni, + + // Below Regster interface can be changed + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // To HW + output gpio_reg_pkg::gpio_reg2hw_t reg2hw, // Write + input gpio_reg_pkg::gpio_hw2reg_t hw2reg, // Read + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import gpio_reg_pkg::* ; + + localparam int AW = 6; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + tlul_pkg::tl_h2d_t tl_reg_h2d; + tlul_pkg::tl_d2h_t tl_reg_d2h; + + assign tl_reg_h2d = tl_i; + assign tl_o = tl_reg_d2h; + + tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW) + ) u_reg_if ( + .clk_i, + .rst_ni, + + .tl_i (tl_reg_h2d), + .tl_o (tl_reg_d2h), + + .we_o (reg_we), + .re_o (reg_re), + .addr_o (reg_addr), + .wdata_o (reg_wdata), + .be_o (reg_be), + .rdata_i (reg_rdata), + .error_i (reg_error) + ); + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err ; + + // Define SW related signals + // Format: <reg>_<field>_{wd|we|qs} + // or <reg>_{wd|we|qs} if field == 1 or 0 + logic [31:0] intr_state_qs; + logic [31:0] intr_state_wd; + logic intr_state_we; + logic [31:0] intr_enable_qs; + logic [31:0] intr_enable_wd; + logic intr_enable_we; + logic [31:0] intr_test_wd; + logic intr_test_we; + logic [31:0] data_in_qs; + logic [31:0] direct_out_qs; + logic [31:0] direct_out_wd; + logic direct_out_we; + logic direct_out_re; + logic [15:0] masked_out_lower_data_qs; + logic [15:0] masked_out_lower_data_wd; + logic masked_out_lower_data_we; + logic masked_out_lower_data_re; + logic [15:0] masked_out_lower_mask_wd; + logic masked_out_lower_mask_we; + logic [15:0] masked_out_upper_data_qs; + logic [15:0] masked_out_upper_data_wd; + logic masked_out_upper_data_we; + logic masked_out_upper_data_re; + logic [15:0] masked_out_upper_mask_wd; + logic masked_out_upper_mask_we; + logic [31:0] direct_oe_qs; + logic [31:0] direct_oe_wd; + logic direct_oe_we; + logic direct_oe_re; + logic [15:0] masked_oe_lower_data_qs; + logic [15:0] masked_oe_lower_data_wd; + logic masked_oe_lower_data_we; + logic masked_oe_lower_data_re; + logic [15:0] masked_oe_lower_mask_qs; + logic [15:0] masked_oe_lower_mask_wd; + logic masked_oe_lower_mask_we; + logic masked_oe_lower_mask_re; + logic [15:0] masked_oe_upper_data_qs; + logic [15:0] masked_oe_upper_data_wd; + logic masked_oe_upper_data_we; + logic masked_oe_upper_data_re; + logic [15:0] masked_oe_upper_mask_qs; + logic [15:0] masked_oe_upper_mask_wd; + logic masked_oe_upper_mask_we; + logic masked_oe_upper_mask_re; + logic [31:0] intr_ctrl_en_rising_qs; + logic [31:0] intr_ctrl_en_rising_wd; + logic intr_ctrl_en_rising_we; + logic [31:0] intr_ctrl_en_falling_qs; + logic [31:0] intr_ctrl_en_falling_wd; + logic intr_ctrl_en_falling_we; + logic [31:0] intr_ctrl_en_lvlhigh_qs; + logic [31:0] intr_ctrl_en_lvlhigh_wd; + logic intr_ctrl_en_lvlhigh_we; + logic [31:0] intr_ctrl_en_lvllow_qs; + logic [31:0] intr_ctrl_en_lvllow_wd; + logic intr_ctrl_en_lvllow_we; + logic [31:0] ctrl_en_input_filter_qs; + logic [31:0] ctrl_en_input_filter_wd; + logic ctrl_en_input_filter_we; + + // Register instances + // R[intr_state]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("W1C"), + .RESVAL (32'h0) + ) u_intr_state ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_we), + .wd (intr_state_wd), + + // from internal hardware + .de (hw2reg.intr_state.de), + .d (hw2reg.intr_state.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.q ), + + // to register interface (read) + .qs (intr_state_qs) + ); + + + // R[intr_enable]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_intr_enable ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_we), + .wd (intr_enable_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.q ), + + // to register interface (read) + .qs (intr_enable_qs) + ); + + + // R[intr_test]: V(True) + + prim_subreg_ext #( + .DW (32) + ) u_intr_test ( + .re (1'b0), + .we (intr_test_we), + .wd (intr_test_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.qe), + .q (reg2hw.intr_test.q ), + .qs () + ); + + + // R[data_in]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RO"), + .RESVAL (32'h0) + ) u_data_in ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.data_in.de), + .d (hw2reg.data_in.d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (data_in_qs) + ); + + + // R[direct_out]: V(True) + + prim_subreg_ext #( + .DW (32) + ) u_direct_out ( + .re (direct_out_re), + .we (direct_out_we), + .wd (direct_out_wd), + .d (hw2reg.direct_out.d), + .qre (), + .qe (reg2hw.direct_out.qe), + .q (reg2hw.direct_out.q ), + .qs (direct_out_qs) + ); + + + // R[masked_out_lower]: V(True) + + // F[data]: 15:0 + prim_subreg_ext #( + .DW (16) + ) u_masked_out_lower_data ( + .re (masked_out_lower_data_re), + .we (masked_out_lower_data_we), + .wd (masked_out_lower_data_wd), + .d (hw2reg.masked_out_lower.data.d), + .qre (), + .qe (reg2hw.masked_out_lower.data.qe), + .q (reg2hw.masked_out_lower.data.q ), + .qs (masked_out_lower_data_qs) + ); + + + // F[mask]: 31:16 + prim_subreg_ext #( + .DW (16) + ) u_masked_out_lower_mask ( + .re (1'b0), + .we (masked_out_lower_mask_we), + .wd (masked_out_lower_mask_wd), + .d (hw2reg.masked_out_lower.mask.d), + .qre (), + .qe (reg2hw.masked_out_lower.mask.qe), + .q (reg2hw.masked_out_lower.mask.q ), + .qs () + ); + + + // R[masked_out_upper]: V(True) + + // F[data]: 15:0 + prim_subreg_ext #( + .DW (16) + ) u_masked_out_upper_data ( + .re (masked_out_upper_data_re), + .we (masked_out_upper_data_we), + .wd (masked_out_upper_data_wd), + .d (hw2reg.masked_out_upper.data.d), + .qre (), + .qe (reg2hw.masked_out_upper.data.qe), + .q (reg2hw.masked_out_upper.data.q ), + .qs (masked_out_upper_data_qs) + ); + + + // F[mask]: 31:16 + prim_subreg_ext #( + .DW (16) + ) u_masked_out_upper_mask ( + .re (1'b0), + .we (masked_out_upper_mask_we), + .wd (masked_out_upper_mask_wd), + .d (hw2reg.masked_out_upper.mask.d), + .qre (), + .qe (reg2hw.masked_out_upper.mask.qe), + .q (reg2hw.masked_out_upper.mask.q ), + .qs () + ); + + + // R[direct_oe]: V(True) + + prim_subreg_ext #( + .DW (32) + ) u_direct_oe ( + .re (direct_oe_re), + .we (direct_oe_we), + .wd (direct_oe_wd), + .d (hw2reg.direct_oe.d), + .qre (), + .qe (reg2hw.direct_oe.qe), + .q (reg2hw.direct_oe.q ), + .qs (direct_oe_qs) + ); + + + // R[masked_oe_lower]: V(True) + + // F[data]: 15:0 + prim_subreg_ext #( + .DW (16) + ) u_masked_oe_lower_data ( + .re (masked_oe_lower_data_re), + .we (masked_oe_lower_data_we), + .wd (masked_oe_lower_data_wd), + .d (hw2reg.masked_oe_lower.data.d), + .qre (), + .qe (reg2hw.masked_oe_lower.data.qe), + .q (reg2hw.masked_oe_lower.data.q ), + .qs (masked_oe_lower_data_qs) + ); + + + // F[mask]: 31:16 + prim_subreg_ext #( + .DW (16) + ) u_masked_oe_lower_mask ( + .re (masked_oe_lower_mask_re), + .we (masked_oe_lower_mask_we), + .wd (masked_oe_lower_mask_wd), + .d (hw2reg.masked_oe_lower.mask.d), + .qre (), + .qe (reg2hw.masked_oe_lower.mask.qe), + .q (reg2hw.masked_oe_lower.mask.q ), + .qs (masked_oe_lower_mask_qs) + ); + + + // R[masked_oe_upper]: V(True) + + // F[data]: 15:0 + prim_subreg_ext #( + .DW (16) + ) u_masked_oe_upper_data ( + .re (masked_oe_upper_data_re), + .we (masked_oe_upper_data_we), + .wd (masked_oe_upper_data_wd), + .d (hw2reg.masked_oe_upper.data.d), + .qre (), + .qe (reg2hw.masked_oe_upper.data.qe), + .q (reg2hw.masked_oe_upper.data.q ), + .qs (masked_oe_upper_data_qs) + ); + + + // F[mask]: 31:16 + prim_subreg_ext #( + .DW (16) + ) u_masked_oe_upper_mask ( + .re (masked_oe_upper_mask_re), + .we (masked_oe_upper_mask_we), + .wd (masked_oe_upper_mask_wd), + .d (hw2reg.masked_oe_upper.mask.d), + .qre (), + .qe (reg2hw.masked_oe_upper.mask.qe), + .q (reg2hw.masked_oe_upper.mask.q ), + .qs (masked_oe_upper_mask_qs) + ); + + + // R[intr_ctrl_en_rising]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_intr_ctrl_en_rising ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_ctrl_en_rising_we), + .wd (intr_ctrl_en_rising_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_ctrl_en_rising.q ), + + // to register interface (read) + .qs (intr_ctrl_en_rising_qs) + ); + + + // R[intr_ctrl_en_falling]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_intr_ctrl_en_falling ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_ctrl_en_falling_we), + .wd (intr_ctrl_en_falling_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_ctrl_en_falling.q ), + + // to register interface (read) + .qs (intr_ctrl_en_falling_qs) + ); + + + // R[intr_ctrl_en_lvlhigh]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_intr_ctrl_en_lvlhigh ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_ctrl_en_lvlhigh_we), + .wd (intr_ctrl_en_lvlhigh_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_ctrl_en_lvlhigh.q ), + + // to register interface (read) + .qs (intr_ctrl_en_lvlhigh_qs) + ); + + + // R[intr_ctrl_en_lvllow]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_intr_ctrl_en_lvllow ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_ctrl_en_lvllow_we), + .wd (intr_ctrl_en_lvllow_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_ctrl_en_lvllow.q ), + + // to register interface (read) + .qs (intr_ctrl_en_lvllow_qs) + ); + + + // R[ctrl_en_input_filter]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_ctrl_en_input_filter ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_en_input_filter_we), + .wd (ctrl_en_input_filter_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl_en_input_filter.q ), + + // to register interface (read) + .qs (ctrl_en_input_filter_qs) + ); + + + + + logic [14:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[ 0] = (reg_addr == GPIO_INTR_STATE_OFFSET); + addr_hit[ 1] = (reg_addr == GPIO_INTR_ENABLE_OFFSET); + addr_hit[ 2] = (reg_addr == GPIO_INTR_TEST_OFFSET); + addr_hit[ 3] = (reg_addr == GPIO_DATA_IN_OFFSET); + addr_hit[ 4] = (reg_addr == GPIO_DIRECT_OUT_OFFSET); + addr_hit[ 5] = (reg_addr == GPIO_MASKED_OUT_LOWER_OFFSET); + addr_hit[ 6] = (reg_addr == GPIO_MASKED_OUT_UPPER_OFFSET); + addr_hit[ 7] = (reg_addr == GPIO_DIRECT_OE_OFFSET); + addr_hit[ 8] = (reg_addr == GPIO_MASKED_OE_LOWER_OFFSET); + addr_hit[ 9] = (reg_addr == GPIO_MASKED_OE_UPPER_OFFSET); + addr_hit[10] = (reg_addr == GPIO_INTR_CTRL_EN_RISING_OFFSET); + addr_hit[11] = (reg_addr == GPIO_INTR_CTRL_EN_FALLING_OFFSET); + addr_hit[12] = (reg_addr == GPIO_INTR_CTRL_EN_LVLHIGH_OFFSET); + addr_hit[13] = (reg_addr == GPIO_INTR_CTRL_EN_LVLLOW_OFFSET); + addr_hit[14] = (reg_addr == GPIO_CTRL_EN_INPUT_FILTER_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = 1'b0; + if (addr_hit[ 0] && reg_we && (GPIO_PERMIT[ 0] != (GPIO_PERMIT[ 0] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 1] && reg_we && (GPIO_PERMIT[ 1] != (GPIO_PERMIT[ 1] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 2] && reg_we && (GPIO_PERMIT[ 2] != (GPIO_PERMIT[ 2] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 3] && reg_we && (GPIO_PERMIT[ 3] != (GPIO_PERMIT[ 3] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 4] && reg_we && (GPIO_PERMIT[ 4] != (GPIO_PERMIT[ 4] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 5] && reg_we && (GPIO_PERMIT[ 5] != (GPIO_PERMIT[ 5] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 6] && reg_we && (GPIO_PERMIT[ 6] != (GPIO_PERMIT[ 6] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 7] && reg_we && (GPIO_PERMIT[ 7] != (GPIO_PERMIT[ 7] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 8] && reg_we && (GPIO_PERMIT[ 8] != (GPIO_PERMIT[ 8] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 9] && reg_we && (GPIO_PERMIT[ 9] != (GPIO_PERMIT[ 9] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[10] && reg_we && (GPIO_PERMIT[10] != (GPIO_PERMIT[10] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[11] && reg_we && (GPIO_PERMIT[11] != (GPIO_PERMIT[11] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[12] && reg_we && (GPIO_PERMIT[12] != (GPIO_PERMIT[12] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[13] && reg_we && (GPIO_PERMIT[13] != (GPIO_PERMIT[13] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[14] && reg_we && (GPIO_PERMIT[14] != (GPIO_PERMIT[14] & reg_be))) wr_err = 1'b1 ; + end + + assign intr_state_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_wd = reg_wdata[31:0]; + + assign intr_enable_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_wd = reg_wdata[31:0]; + + assign intr_test_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_wd = reg_wdata[31:0]; + + + assign direct_out_we = addr_hit[4] & reg_we & ~wr_err; + assign direct_out_wd = reg_wdata[31:0]; + assign direct_out_re = addr_hit[4] && reg_re; + + assign masked_out_lower_data_we = addr_hit[5] & reg_we & ~wr_err; + assign masked_out_lower_data_wd = reg_wdata[15:0]; + assign masked_out_lower_data_re = addr_hit[5] && reg_re; + + assign masked_out_lower_mask_we = addr_hit[5] & reg_we & ~wr_err; + assign masked_out_lower_mask_wd = reg_wdata[31:16]; + + assign masked_out_upper_data_we = addr_hit[6] & reg_we & ~wr_err; + assign masked_out_upper_data_wd = reg_wdata[15:0]; + assign masked_out_upper_data_re = addr_hit[6] && reg_re; + + assign masked_out_upper_mask_we = addr_hit[6] & reg_we & ~wr_err; + assign masked_out_upper_mask_wd = reg_wdata[31:16]; + + assign direct_oe_we = addr_hit[7] & reg_we & ~wr_err; + assign direct_oe_wd = reg_wdata[31:0]; + assign direct_oe_re = addr_hit[7] && reg_re; + + assign masked_oe_lower_data_we = addr_hit[8] & reg_we & ~wr_err; + assign masked_oe_lower_data_wd = reg_wdata[15:0]; + assign masked_oe_lower_data_re = addr_hit[8] && reg_re; + + assign masked_oe_lower_mask_we = addr_hit[8] & reg_we & ~wr_err; + assign masked_oe_lower_mask_wd = reg_wdata[31:16]; + assign masked_oe_lower_mask_re = addr_hit[8] && reg_re; + + assign masked_oe_upper_data_we = addr_hit[9] & reg_we & ~wr_err; + assign masked_oe_upper_data_wd = reg_wdata[15:0]; + assign masked_oe_upper_data_re = addr_hit[9] && reg_re; + + assign masked_oe_upper_mask_we = addr_hit[9] & reg_we & ~wr_err; + assign masked_oe_upper_mask_wd = reg_wdata[31:16]; + assign masked_oe_upper_mask_re = addr_hit[9] && reg_re; + + assign intr_ctrl_en_rising_we = addr_hit[10] & reg_we & ~wr_err; + assign intr_ctrl_en_rising_wd = reg_wdata[31:0]; + + assign intr_ctrl_en_falling_we = addr_hit[11] & reg_we & ~wr_err; + assign intr_ctrl_en_falling_wd = reg_wdata[31:0]; + + assign intr_ctrl_en_lvlhigh_we = addr_hit[12] & reg_we & ~wr_err; + assign intr_ctrl_en_lvlhigh_wd = reg_wdata[31:0]; + + assign intr_ctrl_en_lvllow_we = addr_hit[13] & reg_we & ~wr_err; + assign intr_ctrl_en_lvllow_wd = reg_wdata[31:0]; + + assign ctrl_en_input_filter_we = addr_hit[14] & reg_we & ~wr_err; + assign ctrl_en_input_filter_wd = reg_wdata[31:0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[31:0] = intr_state_qs; + end + + addr_hit[1]: begin + reg_rdata_next[31:0] = intr_enable_qs; + end + + addr_hit[2]: begin + reg_rdata_next[31:0] = '0; + end + + addr_hit[3]: begin + reg_rdata_next[31:0] = data_in_qs; + end + + addr_hit[4]: begin + reg_rdata_next[31:0] = direct_out_qs; + end + + addr_hit[5]: begin + reg_rdata_next[15:0] = masked_out_lower_data_qs; + reg_rdata_next[31:16] = '0; + end + + addr_hit[6]: begin + reg_rdata_next[15:0] = masked_out_upper_data_qs; + reg_rdata_next[31:16] = '0; + end + + addr_hit[7]: begin + reg_rdata_next[31:0] = direct_oe_qs; + end + + addr_hit[8]: begin + reg_rdata_next[15:0] = masked_oe_lower_data_qs; + reg_rdata_next[31:16] = masked_oe_lower_mask_qs; + end + + addr_hit[9]: begin + reg_rdata_next[15:0] = masked_oe_upper_data_qs; + reg_rdata_next[31:16] = masked_oe_upper_mask_qs; + end + + addr_hit[10]: begin + reg_rdata_next[31:0] = intr_ctrl_en_rising_qs; + end + + addr_hit[11]: begin + reg_rdata_next[31:0] = intr_ctrl_en_falling_qs; + end + + addr_hit[12]: begin + reg_rdata_next[31:0] = intr_ctrl_en_lvlhigh_qs; + end + + addr_hit[13]: begin + reg_rdata_next[31:0] = intr_ctrl_en_lvllow_qs; + end + + addr_hit[14]: begin + reg_rdata_next[31:0] = ctrl_en_input_filter_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end +endmodule
diff --git a/verilog/rtl/iccm_controller.v b/verilog/rtl/iccm_controller.v new file mode 100644 index 0000000..99a58ef --- /dev/null +++ b/verilog/rtl/iccm_controller.v
@@ -0,0 +1,135 @@ + + +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +module iccm_controller ( + clk_i, + rst_ni, + prog_i, + rx_dv_i, + rx_byte_i, + we_o, + addr_o, + wdata_o, + reset_o +); + input wire clk_i; + input wire rst_ni; + input wire prog_i; + input wire rx_dv_i; + input wire [7:0] rx_byte_i; + output wire we_o; + output wire [11:0] addr_o; + output wire [31:0] wdata_o; + output wire reset_o; + reg [1:0] ctrl_fsm_cs; + reg [1:0] ctrl_fsm_ns; + wire [7:0] rx_byte_d; + reg [7:0] rx_byte_q0; + reg [7:0] rx_byte_q1; + reg [7:0] rx_byte_q2; + reg [7:0] rx_byte_q3; + reg we_q; + reg we_d; + reg [11:0] addr_q; + reg [11:0] addr_d; + reg reset_q; + reg reset_d; + reg [1:0] byte_count; + localparam [1:0] DONE = 3; + localparam [1:0] LOAD = 1; + localparam [1:0] PROG = 2; + localparam [1:0] RESET = 0; + always @(*) begin + we_d = we_q; + addr_d = addr_q; + reset_d = reset_q; + ctrl_fsm_ns = ctrl_fsm_cs; + case (ctrl_fsm_cs) + RESET: begin + we_d = 1'b0; + reset_d = 1'b0; + if (rx_dv_i) + ctrl_fsm_ns = LOAD; + else + ctrl_fsm_ns = RESET; + end + LOAD: + if (((byte_count == 2'b11) && (rx_byte_q2 != 8'h0f)) && (rx_byte_d != 8'hff)) begin + we_d = 1'b1; + ctrl_fsm_ns = PROG; + end + else + ctrl_fsm_ns = DONE; + PROG: begin + we_d = 1'b0; + ctrl_fsm_ns = DONE; + end + DONE: + if (wdata_o == 32'h00000fff || (!rst_ni)) begin + ctrl_fsm_ns = DONE; + reset_d = 1'b1; + end + else if (rx_dv_i) + ctrl_fsm_ns = LOAD; + else + ctrl_fsm_ns = DONE; + // default: ctrl_fsm_ns = RESET; + endcase + end + assign rx_byte_d = rx_byte_i; + assign we_o = we_q; + assign addr_o = addr_q; + assign wdata_o = {rx_byte_q0, rx_byte_q1, rx_byte_q2, rx_byte_q3}; + assign reset_o = reset_q; + always @(posedge clk_i or negedge rst_ni) + if (!rst_ni) begin + we_q <= 1'b0; + addr_q <= 12'b000000000000; + rx_byte_q0 <= 8'b00000000; + rx_byte_q1 <= 8'b00000000; + rx_byte_q2 <= 8'b00000000; + rx_byte_q3 <= 8'b00000000; + reset_q <= 1'b1; + byte_count <= 2'b00; + ctrl_fsm_cs <= DONE; + end + else if (prog_i) begin + we_q <= 1'b0; + addr_q <= 12'b000000000000; + rx_byte_q0 <= 8'b00000000; + rx_byte_q1 <= 8'b00000000; + rx_byte_q2 <= 8'b00000000; + rx_byte_q3 <= 8'b00000000; + reset_q <= 1'b0; + byte_count <= 2'b00; + ctrl_fsm_cs <= RESET; + end + else begin + we_q <= we_d; + if (ctrl_fsm_cs == LOAD) begin + if (byte_count == 2'b00) begin + rx_byte_q0 <= rx_byte_d; + byte_count <= 2'b01; + end + else if (byte_count == 2'b01) begin + rx_byte_q1 <= rx_byte_d; + byte_count <= 2'b10; + end + else if (byte_count == 2'b10) begin + rx_byte_q2 <= rx_byte_d; + byte_count <= 2'b11; + end + else begin + rx_byte_q3 <= rx_byte_d; + byte_count <= 2'b00; + end + addr_q <= addr_d; + end + if (ctrl_fsm_cs == PROG) + addr_q <= addr_d + 1'b1; + reset_q <= reset_d; + ctrl_fsm_cs <= ctrl_fsm_ns; + end +endmodule
diff --git a/verilog/rtl/instr_mem_top.sv b/verilog/rtl/instr_mem_top.sv new file mode 100644 index 0000000..9619890 --- /dev/null +++ b/verilog/rtl/instr_mem_top.sv
@@ -0,0 +1,82 @@ +module instr_mem_top +( + input clk_i, + input rst_ni, + + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, +// iccm controller interface + input [11:0] iccm_ctrl_addr, + input [31:0] iccm_ctrl_wdata, + input iccm_ctrl_we, + input prog_rst_ni, + + +// sram interface + output logic csb, + output logic [11:0] addr_o, + output logic [31:0] wdata_o, + output logic [3:0] wmask_o, + output logic we_o, + input logic [31:0] rdata_i +); + + +logic rvalid; + +logic tl_we; +logic [31:0] tl_wmask; +logic [31:0] tl_wdata; +logic [11:0] tl_addr; +logic tl_req; +logic [3:0] mask_sel; + +assign mask_sel[0] = (tl_wmask[7:0] != 8'b0) ? 1'b1: 1'b0; +assign mask_sel[1] = (tl_wmask[15:8] != 8'b0) ? 1'b1: 1'b0; +assign mask_sel[2] = (tl_wmask[23:16] != 8'b0) ? 1'b1: 2'b0; +assign mask_sel[3] = (tl_wmask[31:24] != 8'b0) ? 1'b1: 2'b0; + +assign csb = ~(tl_req | iccm_ctrl_we); + +assign addr_o = (prog_rst_ni) ? tl_addr : iccm_ctrl_addr; +assign wdata_o = (prog_rst_ni) ? tl_wdata : iccm_ctrl_wdata; +assign we_o = ~((prog_rst_ni) ? tl_we : iccm_ctrl_we); +assign wmask_o = (prog_rst_ni) ? mask_sel : 4'b1111; + + + tlul_sram_adapter #( + .SramAw (12), + .SramDw (32), + .Outstanding (2), + .ByteAccess (1), + .ErrOnWrite (0), // 1: Writes not allowed, automatically error + .ErrOnRead (0) // 1: Reads not allowed, automatically error + +) inst_mem ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_i (tl_i), + .tl_o (tl_o), + .req_o (tl_req), + .gnt_i (1'b1), + .we_o (tl_we), + .addr_o (tl_addr), + .wdata_o (tl_wdata), + .wmask_o (tl_wmask), + .rdata_i ((rst_ni) ? rdata_i: '0), + .rvalid_i (rvalid), + .rerror_i (2'b0) +); + + always_ff @(posedge clk_i) begin + if (!rst_ni) begin + rvalid <= 1'b0; + end else if (iccm_ctrl_we | tl_we) begin + rvalid <= 1'b0; + end else begin + rvalid <= tl_req; + end + end + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/iteration_div_sqrt_mvp.sv b/verilog/rtl/iteration_div_sqrt_mvp.sv new file mode 100644 index 0000000..0c645e6 --- /dev/null +++ b/verilog/rtl/iteration_div_sqrt_mvp.sv
@@ -0,0 +1,61 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +//////////////////////////////////////////////////////////////////////////////// +// Company: IIS @ ETHZ - Federal Institute of Technology // +// // +// Engineers: Lei Li lile@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 12/01/2017 // +// Design Name: FPU // +// Module Name: iteration_div_sqrt_mvp // +// Project Name: Private FPU // +// Language: SystemVerilog // +// // +// Description: iteration unit for div and sqrt // +// // +// // +// Revision: 03/14/2018 // +// For div_sqrt_mvp // +//////////////////////////////////////////////////////////////////////////////// + +module iteration_div_sqrt_mvp +#( + parameter WIDTH=25 +) + (//Input + + input logic [WIDTH-1:0] A_DI, + input logic [WIDTH-1:0] B_DI, + input logic Div_enable_SI, + input logic Div_start_dly_SI, + input logic Sqrt_enable_SI, + input logic [1:0] D_DI, + + output logic [1:0] D_DO, + output logic [WIDTH-1:0] Sum_DO, + output logic Carry_out_DO + ); + + logic D_carry_D; + logic Sqrt_cin_D; + logic Cin_D; + + assign D_DO[0]=~D_DI[0]; + assign D_DO[1]=~(D_DI[1] ^ D_DI[0]); + assign D_carry_D=D_DI[1] | D_DI[0]; + assign Sqrt_cin_D=Sqrt_enable_SI&&D_carry_D; + assign Cin_D=Div_enable_SI?1'b0:Sqrt_cin_D; + assign {Carry_out_DO,Sum_DO}=A_DI+B_DI+Cin_D; + +endmodule
diff --git a/verilog/rtl/jtag_pkg.sv b/verilog/rtl/jtag_pkg.sv new file mode 100644 index 0000000..2a67ee0 --- /dev/null +++ b/verilog/rtl/jtag_pkg.sv
@@ -0,0 +1,24 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + +package jtag_pkg; + + typedef struct packed { + logic tck; + logic tms; + logic trst_n; + logic tdi; + } jtag_req_t; + + parameter jtag_req_t JTAG_REQ_DEFAULT = '0; + + typedef struct packed { + logic tdo; + logic tdo_oe; + } jtag_rsp_t; + + parameter jtag_rsp_t JTAG_RSP_DEFAULT = '0; + +endpackage : jtag_pkg
diff --git a/verilog/rtl/lzc.sv b/verilog/rtl/lzc.sv new file mode 100644 index 0000000..424eb2e --- /dev/null +++ b/verilog/rtl/lzc.sv
@@ -0,0 +1,112 @@ +// Copyright (c) 2018 - 2019 ETH Zurich, University of Bologna +// All rights reserved. +// +// This code is under development and not yet released to the public. +// Until it is released, the code is under the copyright of ETH Zurich and +// the University of Bologna, and may contain confidential and/or unpublished +// work. Any reuse/redistribution is strictly forbidden without written +// permission from ETH Zurich. +// +// Bug fixes and contributions will eventually be released under the +// SolderPad open hardware license in the context of the PULP platform +// (http://www.pulp-platform.org), under the copyright of ETH Zurich and the +// University of Bologna. + +/// A trailing zero counter / leading zero counter. +/// Set MODE to 0 for trailing zero counter => cnt_o is the number of trailing zeros (from the LSB) +/// Set MODE to 1 for leading zero counter => cnt_o is the number of leading zeros (from the MSB) +/// If the input does not contain a zero, `empty_o` is asserted. Additionally `cnt_o` contains +/// the maximum number of zeros - 1. For example: +/// in_i = 000_0000, empty_o = 1, cnt_o = 6 (mode = 0) +/// in_i = 000_0001, empty_o = 0, cnt_o = 0 (mode = 0) +/// in_i = 000_1000, empty_o = 0, cnt_o = 3 (mode = 0) +/// Furthermore, this unit contains a more efficient implementation for Verilator (simulation only). +/// This speeds up simulation significantly. +module lzc #( + /// The width of the input vector. + parameter int unsigned WIDTH = 2, + /// Mode selection: 0 -> trailing zero, 1 -> leading zero + parameter bit MODE = 1'b0, + /// Dependent parameter. Do **not** change! + /// + /// Width of the output signal with the zero count. + parameter int unsigned CNT_WIDTH = cf_math_pkg::idx_width(WIDTH) +) ( + /// Input vector to be counted. + input logic [WIDTH-1:0] in_i, + /// Count of the leading / trailing zeros. + output logic [CNT_WIDTH-1:0] cnt_o, + /// Counter is empty: Asserted if all bits in in_i are zero. + output logic empty_o +); + + if (WIDTH == 1) begin : gen_degenerate_lzc + + assign cnt_o[0] = !in_i[0]; + assign empty_o = !in_i[0]; + + end else begin : gen_lzc + + localparam int unsigned NumLevels = $clog2(WIDTH); + + // pragma translate_off + initial begin + assert(WIDTH > 0) else $fatal(1, "input must be at least one bit wide"); + end + // pragma translate_on + + logic [WIDTH-1:0][NumLevels-1:0] index_lut; + logic [2**NumLevels-1:0] sel_nodes; + logic [2**NumLevels-1:0][NumLevels-1:0] index_nodes; + + logic [WIDTH-1:0] in_tmp; + + // reverse vector if required + always_comb begin : flip_vector + for (int unsigned i = 0; i < WIDTH; i++) begin + in_tmp[i] = (MODE) ? in_i[WIDTH-1-i] : in_i[i]; + end + end + + for (genvar j = 0; unsigned'(j) < WIDTH; j++) begin : g_index_lut + assign index_lut[j] = (NumLevels)'(unsigned'(j)); + end + + for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : g_levels + if (unsigned'(level) == NumLevels - 1) begin : g_last_level + for (genvar k = 0; k < 2 ** level; k++) begin : g_level + // if two successive indices are still in the vector... + if (unsigned'(k) * 2 < WIDTH - 1) begin : g_reduce + assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2] | in_tmp[k * 2 + 1]; + assign index_nodes[2 ** level - 1 + k] = (in_tmp[k * 2] == 1'b1) + ? index_lut[k * 2] : + index_lut[k * 2 + 1]; + end + // if only the first index is still in the vector... + if (unsigned'(k) * 2 == WIDTH - 1) begin : g_base + assign sel_nodes[2 ** level - 1 + k] = in_tmp[k * 2]; + assign index_nodes[2 ** level - 1 + k] = index_lut[k * 2]; + end + // if index is out of range + if (unsigned'(k) * 2 > WIDTH - 1) begin : g_out_of_range + assign sel_nodes[2 ** level - 1 + k] = 1'b0; + assign index_nodes[2 ** level - 1 + k] = '0; + end + end + end else begin : g_not_last_level + for (genvar l = 0; l < 2 ** level; l++) begin : g_level + assign sel_nodes[2 ** level - 1 + l] = + sel_nodes[2 ** (level + 1) - 1 + l * 2] | sel_nodes[2 ** (level + 1) - 1 + l * 2 + 1]; + assign index_nodes[2 ** level - 1 + l] = (sel_nodes[2 ** (level + 1) - 1 + l * 2] == 1'b1) + ? index_nodes[2 ** (level + 1) - 1 + l * 2] : + index_nodes[2 ** (level + 1) - 1 + l * 2 + 1]; + end + end + end + + assign cnt_o = NumLevels > unsigned'(0) ? index_nodes[0] : {($clog2(WIDTH)) {1'b0}}; + assign empty_o = NumLevels > unsigned'(0) ? ~sel_nodes[0] : ~(|in_i); + + end : gen_lzc + +endmodule : lzc
diff --git a/verilog/rtl/minus_one.v b/verilog/rtl/minus_one.v new file mode 100644 index 0000000..f3575c9 --- /dev/null +++ b/verilog/rtl/minus_one.v
@@ -0,0 +1,122 @@ +/*Author: Zhuxu + m99a1@yahoo.cn +Use parallel prefix tree structure to reduce a 16-bit number by one. + +stage 0: number of genration=16; number of logic operation=16; G_0[xx]=~i_operand[xx]; +stage 1: NOG=16; NOO=8; G_1[2n-1]=G_0[2n-1]&&G_0[2n-2]; n=8:1 +stage 2: NOG=16; NOO=7; G_2[2n-1]=G_1[2n-1]&&G_1[2n-3]; n=8:2 +stage 3: NOG=16; NOO=6; G_3[2n-1]=G_2[2n-1]&&G_2[2n-5]; n=8:3 +stage 4: NOG=16; NOO=4; G_4[2n-1]=G_3[2n-1]&&G_3[2n-9]; n=8:5 +stage 5: NOG=16; NOO=7; G_5[2n]=G_4[2n]&&G_4[2n-1]; n=7:1 + +*/ +module minus_one( +input [15:0]i_operand, +output [15:0]o_result, +output o_borrow +); +//stage 0 +wire [15:0]G_0; +assign G_0=~i_operand; + +//stage 1 +wire [15:0]G_1; +assign G_1[1]=G_0[1]&G_0[0]; +assign G_1[3]=G_0[3]&G_0[2]; +assign G_1[5]=G_0[5]&G_0[4]; +assign G_1[7]=G_0[7]&G_0[6]; +assign G_1[9]=G_0[9]&G_0[8]; +assign G_1[11]=G_0[11]&G_0[10]; +assign G_1[13]=G_0[13]&G_0[12]; +assign G_1[15]=G_0[15]&G_0[14]; +assign G_1[0]=G_0[0]; +assign G_1[2]=G_0[2]; +assign G_1[4]=G_0[4]; +assign G_1[6]=G_0[6]; +assign G_1[8]=G_0[8]; +assign G_1[10]=G_0[10]; +assign G_1[12]=G_0[12]; +assign G_1[14]=G_0[14]; + +//stage 2 +wire [15:0]G_2; +assign G_2[3]=G_1[3]&G_1[1]; +assign G_2[5]=G_1[5]&G_1[3]; +assign G_2[7]=G_1[7]&G_1[5]; +assign G_2[9]=G_1[9]&G_1[7]; +assign G_2[11]=G_1[11]&G_1[9]; +assign G_2[13]=G_1[13]&G_1[11]; +assign G_2[15]=G_1[15]&G_1[13]; +assign G_2[0]=G_1[0]; +assign G_2[2]=G_1[2]; +assign G_2[1]=G_1[1]; +assign G_2[4]=G_1[4]; +assign G_2[6]=G_1[6]; +assign G_2[8]=G_1[8]; +assign G_2[10]=G_1[10]; +assign G_2[12]=G_1[12]; +assign G_2[14]=G_1[14]; + +//stage 3 +wire [15:0]G_3; +assign G_3[5]=G_2[5]&G_2[1]; +assign G_3[7]=G_2[7]&G_2[3]; +assign G_3[9]=G_2[9]&G_2[5]; +assign G_3[11]=G_2[11]&G_2[7]; +assign G_3[13]=G_2[13]&G_2[9]; +assign G_3[15]=G_2[15]&G_2[11]; +assign G_3[0]=G_2[0]; +assign G_3[2]=G_2[2]; +assign G_3[1]=G_2[1]; +assign G_3[4]=G_2[4]; +assign G_3[3]=G_2[3]; +assign G_3[6]=G_2[6]; +assign G_3[8]=G_2[8]; +assign G_3[10]=G_2[10]; +assign G_3[12]=G_2[12]; +assign G_3[14]=G_2[14]; + +//stage 4 +wire [15:0]G_4; +assign G_4[9]=G_3[9]&G_3[1]; +assign G_4[11]=G_3[11]&G_3[3]; +assign G_4[13]=G_3[13]&G_3[5]; +assign G_4[15]=G_3[15]&G_3[7]; +assign G_4[0]=G_3[0]; +assign G_4[2]=G_3[2]; +assign G_4[1]=G_3[1]; +assign G_4[4]=G_3[4]; +assign G_4[3]=G_3[3]; +assign G_4[6]=G_3[6]; +assign G_4[5]=G_3[5]; +assign G_4[8]=G_3[8]; +assign G_4[7]=G_3[7]; +assign G_4[10]=G_3[10]; +assign G_4[12]=G_3[12]; +assign G_4[14]=G_3[14]; + +//stage 5 +wire [15:0]G_5; +assign G_5[2]=G_4[2]&G_4[1]; +assign G_5[4]=G_4[4]&G_4[3]; +assign G_5[6]=G_4[6]&G_4[5]; +assign G_5[8]=G_4[8]&G_4[7]; +assign G_5[10]=G_4[10]&G_4[9]; +assign G_5[12]=G_4[12]&G_4[11]; +assign G_5[14]=G_4[14]&G_4[13]; +assign G_5[1]=G_4[1]; +assign G_5[3]=G_4[3]; +assign G_5[5]=G_4[5]; +assign G_5[7]=G_4[7]; +assign G_5[9]=G_4[9]; +assign G_5[11]=G_4[11]; +assign G_5[13]=G_4[13]; +assign G_5[15]=G_4[15]; +assign G_5[0]=G_4[0]; + +//stage 6 +assign o_result[0]=~i_operand[0]; +assign o_result[15:1]=(G_5[14:0]&(~i_operand[15:1]))|((~G_5[14:0])&i_operand[15:1]); +assign o_borrow=G_5[15]; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/norm_div_sqrt_mvp.sv b/verilog/rtl/norm_div_sqrt_mvp.sv new file mode 100644 index 0000000..590abe9 --- /dev/null +++ b/verilog/rtl/norm_div_sqrt_mvp.sv
@@ -0,0 +1,470 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +//////////////////////////////////////////////////////////////////////////////// +// Company: IIS @ ETHZ - Federal Institute of Technology // +// // +// Engineers: Lei Li lile@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 09/03/2018 // +// Design Name: FPU // +// Module Name: norm_div_sqrt_mvp.sv // +// Project Name: // +// Language: SystemVerilog // +// // +// Description: Floating point Normalizer/Rounding unit // +// Since this module is design as a combinatinal logic, it can// +// be added arbinary register stages for different frequency // +// in the wrapper module. // +// // +// // +// // +// Revision Date: 12/04/2018 // +// Lei Li // +// To address some requirements by Stefan // +// // +// // +// // +// // +// // +// // +//////////////////////////////////////////////////////////////////////////////// + +import defs_div_sqrt_mvp::*; + +module norm_div_sqrt_mvp + (//Inputs + input logic [C_MANT_FP64+4:0] Mant_in_DI, // Include the needed 4-bit for rounding and hidden bit + input logic signed [C_EXP_FP64+1:0] Exp_in_DI, + input logic Sign_in_DI, + input logic Div_enable_SI, + input logic Sqrt_enable_SI, + input logic Inf_a_SI, + input logic Inf_b_SI, + input logic Zero_a_SI, + input logic Zero_b_SI, + input logic NaN_a_SI, + input logic NaN_b_SI, + input logic SNaN_SI, + input logic [C_RM-1:0] RM_SI, + input logic Full_precision_SI, + input logic FP32_SI, + input logic FP64_SI, + input logic FP16_SI, + input logic FP16ALT_SI, + //Outputs + output logic [C_EXP_FP64+C_MANT_FP64:0] Result_DO, + output logic [4:0] Fflags_SO //{NV,DZ,OF,UF,NX} + ); + + + logic Sign_res_D; + + logic NV_OP_S; + logic Exp_OF_S; + logic Exp_UF_S; + logic Div_Zero_S; + logic In_Exact_S; + + ///////////////////////////////////////////////////////////////////////////// + // Normalization // + ///////////////////////////////////////////////////////////////////////////// + logic [C_MANT_FP64:0] Mant_res_norm_D; + logic [C_EXP_FP64-1:0] Exp_res_norm_D; + + ///////////////////////////////////////////////////////////////////////////// + // Right shift operations for negtive exponents // + ///////////////////////////////////////////////////////////////////////////// + + logic [C_EXP_FP64+1:0] Exp_Max_RS_FP64_D; + logic [C_EXP_FP32+1:0] Exp_Max_RS_FP32_D; + logic [C_EXP_FP16+1:0] Exp_Max_RS_FP16_D; + logic [C_EXP_FP16ALT+1:0] Exp_Max_RS_FP16ALT_D; + // + assign Exp_Max_RS_FP64_D=Exp_in_DI[C_EXP_FP64:0]+C_MANT_FP64+1; // to check exponent after (C_MANT_FP64+1)-bit >> when Exp_in_DI is negative + assign Exp_Max_RS_FP32_D=Exp_in_DI[C_EXP_FP32:0]+C_MANT_FP32+1; // to check exponent after (C_MANT_FP32+1)-bit >> when Exp_in_DI is negative + assign Exp_Max_RS_FP16_D=Exp_in_DI[C_EXP_FP16:0]+C_MANT_FP16+1; // to check exponent after (C_MANT_FP16+1)-bit >> when Exp_in_DI is negative + assign Exp_Max_RS_FP16ALT_D=Exp_in_DI[C_EXP_FP16ALT:0]+C_MANT_FP16ALT+1; // to check exponent after (C_MANT_FP16ALT+1)-bit >> when Exp_in_DI is negative + logic [C_EXP_FP64+1:0] Num_RS_D; + assign Num_RS_D=~Exp_in_DI+1+1; // How many right shifts(RS) are needed to generate a denormal number? >> is need only when Exp_in_DI is negative + logic [C_MANT_FP64:0] Mant_RS_D; + logic [C_MANT_FP64+4:0] Mant_forsticky_D; + assign {Mant_RS_D,Mant_forsticky_D} ={Mant_in_DI,{(C_MANT_FP64+1){1'b0}} } >>(Num_RS_D); // +// + logic [C_EXP_FP64+1:0] Exp_subOne_D; + assign Exp_subOne_D = Exp_in_DI -1; + + //normalization + logic [1:0] Mant_lower_D; + logic Mant_sticky_bit_D; + logic [C_MANT_FP64+4:0] Mant_forround_D; + + always_comb + begin + + if(NaN_a_SI) // if a is NaN, return NaN + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D={1'b0,C_MANT_NAN_FP64}; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=1'b0; + NV_OP_S = SNaN_SI; + end + + else if(NaN_b_SI) //if b is NaN, return NaN + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D={1'b0,C_MANT_NAN_FP64}; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=1'b0; + NV_OP_S = SNaN_SI; + end + + else if(Inf_a_SI) + begin + if(Div_enable_SI&&Inf_b_SI) //Inf/Inf, retrurn NaN + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D={1'b0,C_MANT_NAN_FP64}; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=1'b0; + NV_OP_S = 1'b1; + end + else if (Sqrt_enable_SI && Sign_in_DI) begin // catch sqrt(-inf) + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D={1'b0,C_MANT_NAN_FP64}; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=1'b0; + NV_OP_S = 1'b1; + end else begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b1; + Exp_UF_S=1'b0; + Mant_res_norm_D= '0; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + end + + else if(Div_enable_SI&&Inf_b_SI) + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b1; + Exp_UF_S=1'b0; + Mant_res_norm_D= '0; + Exp_res_norm_D='0; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + else if(Zero_a_SI) + begin + if(Div_enable_SI&&Zero_b_SI) + begin + Div_Zero_S=1'b1; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D={1'b0,C_MANT_NAN_FP64}; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=1'b0; + NV_OP_S = 1'b1; + end + else + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D='0; + Exp_res_norm_D='0; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + end + + else if(Div_enable_SI&&(Zero_b_SI)) //div Zero + begin + Div_Zero_S=1'b1; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D='0; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + else if(Sign_in_DI&&Sqrt_enable_SI) //sqrt(-a) + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D={1'b0,C_MANT_NAN_FP64}; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=1'b0; + NV_OP_S = 1'b1; + end + + else if((Exp_in_DI[C_EXP_FP64:0]=='0)) + begin + if(Mant_in_DI!='0) //Exp=0, Mant!=0, it is denormal + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b1; + Mant_res_norm_D={1'b0,Mant_in_DI[C_MANT_FP64+4:5]}; + Exp_res_norm_D='0; + Mant_forround_D={Mant_in_DI[4:0],{(C_MANT_FP64){1'b0}} }; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + else // Zero + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D='0; + Exp_res_norm_D='0; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + end + + else if((Exp_in_DI[C_EXP_FP64:0]==C_EXP_ONE_FP64)&&(~Mant_in_DI[C_MANT_FP64+4])) //denormal + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b1; + Mant_res_norm_D=Mant_in_DI[C_MANT_FP64+4:4]; + Exp_res_norm_D='0; + Mant_forround_D={Mant_in_DI[3:0],{(C_MANT_FP64+1){1'b0}}}; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + else if(Exp_in_DI[C_EXP_FP64+1]) //minus //consider format + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b1; + Mant_res_norm_D={Mant_RS_D[C_MANT_FP64:0]}; + Exp_res_norm_D='0; + Mant_forround_D={Mant_forsticky_D[C_MANT_FP64+4:0]}; //?? + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + else if( (Exp_in_DI[C_EXP_FP32]&&FP32_SI) | (Exp_in_DI[C_EXP_FP64]&&FP64_SI) | (Exp_in_DI[C_EXP_FP16]&&FP16_SI) | (Exp_in_DI[C_EXP_FP16ALT]&&FP16ALT_SI) ) //OF + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b1; + Exp_UF_S=1'b0; + Mant_res_norm_D='0; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + else if( ((Exp_in_DI[C_EXP_FP32-1:0]=='1)&&FP32_SI) | ((Exp_in_DI[C_EXP_FP64-1:0]=='1)&&FP64_SI) | ((Exp_in_DI[C_EXP_FP16-1:0]=='1)&&FP16_SI) | ((Exp_in_DI[C_EXP_FP16ALT-1:0]=='1)&&FP16ALT_SI) )//255 + begin + if(~Mant_in_DI[C_MANT_FP64+4]) // MSB=0 + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D=Mant_in_DI[C_MANT_FP64+3:3]; + Exp_res_norm_D=Exp_subOne_D; + Mant_forround_D={Mant_in_DI[2:0],{(C_MANT_FP64+2){1'b0}}}; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + else if(Mant_in_DI!='0) //NaN + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b1; + Exp_UF_S=1'b0; + Mant_res_norm_D= '0; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + else //infinity + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b1; + Exp_UF_S=1'b0; + Mant_res_norm_D= '0; + Exp_res_norm_D='1; + Mant_forround_D='0; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + end + + else if(Mant_in_DI[C_MANT_FP64+4]) //normal numbers with 1.XXX + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D= Mant_in_DI[C_MANT_FP64+4:4]; + Exp_res_norm_D=Exp_in_DI[C_EXP_FP64-1:0]; + Mant_forround_D={Mant_in_DI[3:0],{(C_MANT_FP64+1){1'b0}}}; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + else //normal numbers with 0.1XX + begin + Div_Zero_S=1'b0; + Exp_OF_S=1'b0; + Exp_UF_S=1'b0; + Mant_res_norm_D=Mant_in_DI[C_MANT_FP64+3:3]; + Exp_res_norm_D=Exp_subOne_D; + Mant_forround_D={Mant_in_DI[2:0],{(C_MANT_FP64+2){1'b0}}}; + Sign_res_D=Sign_in_DI; + NV_OP_S = 1'b0; + end + + end + + ///////////////////////////////////////////////////////////////////////////// + // Rounding enable only for full precision (Full_precision_SI==1'b1) // + ///////////////////////////////////////////////////////////////////////////// + + logic [C_MANT_FP64:0] Mant_upper_D; + logic [C_MANT_FP64+1:0] Mant_upperRounded_D; + logic Mant_roundUp_S; + logic Mant_rounded_S; + + always_comb //determine which bits for Mant_lower_D and Mant_sticky_bit_D + begin + if(FP32_SI) + begin + Mant_upper_D = {Mant_res_norm_D[C_MANT_FP64:C_MANT_FP64-C_MANT_FP32], {(C_MANT_FP64-C_MANT_FP32){1'b0}} }; + Mant_lower_D = Mant_res_norm_D[C_MANT_FP64-C_MANT_FP32-1:C_MANT_FP64-C_MANT_FP32-2]; + Mant_sticky_bit_D = | Mant_res_norm_D[C_MANT_FP64-C_MANT_FP32-3:0]; + end + else if(FP64_SI) + begin + Mant_upper_D = Mant_res_norm_D[C_MANT_FP64:0]; + Mant_lower_D = Mant_forround_D[C_MANT_FP64+4:C_MANT_FP64+3]; + Mant_sticky_bit_D = | Mant_forround_D[C_MANT_FP64+3:0]; + end + else if(FP16_SI) + begin + Mant_upper_D = {Mant_res_norm_D[C_MANT_FP64:C_MANT_FP64-C_MANT_FP16], {(C_MANT_FP64-C_MANT_FP16){1'b0}} }; + Mant_lower_D = Mant_res_norm_D[C_MANT_FP64-C_MANT_FP16-1:C_MANT_FP64-C_MANT_FP16-2]; + Mant_sticky_bit_D = | Mant_res_norm_D[C_MANT_FP64-C_MANT_FP16-3:30]; + end + else //FP16ALT + begin + Mant_upper_D = {Mant_res_norm_D[C_MANT_FP64:C_MANT_FP64-C_MANT_FP16ALT], {(C_MANT_FP64-C_MANT_FP16ALT){1'b0}} }; + Mant_lower_D = Mant_res_norm_D[C_MANT_FP64-C_MANT_FP16ALT-1:C_MANT_FP64-C_MANT_FP16ALT-2]; + Mant_sticky_bit_D = | Mant_res_norm_D[C_MANT_FP64-C_MANT_FP16ALT-3:30]; + end + end + + assign Mant_rounded_S = (|(Mant_lower_D))| Mant_sticky_bit_D; + + + + + always_comb //determine whether to round up or not + begin + Mant_roundUp_S = 1'b0; + case (RM_SI) + C_RM_NEAREST : + Mant_roundUp_S = Mant_lower_D[1] && ((Mant_lower_D[0] | Mant_sticky_bit_D )| ( (FP32_SI&&Mant_upper_D[C_MANT_FP64-C_MANT_FP32]) | (FP64_SI&&Mant_upper_D[0]) | (FP16_SI&&Mant_upper_D[C_MANT_FP64-C_MANT_FP16]) | (FP16ALT_SI&&Mant_upper_D[C_MANT_FP64-C_MANT_FP16ALT]) ) ); + C_RM_TRUNC : + Mant_roundUp_S = 0; + C_RM_PLUSINF : + Mant_roundUp_S = Mant_rounded_S & ~Sign_in_DI; + C_RM_MINUSINF: + Mant_roundUp_S = Mant_rounded_S & Sign_in_DI; + default : + Mant_roundUp_S = 0; + endcase // case (RM_DI) + end // always_comb begin + + logic Mant_renorm_S; + logic [C_MANT_FP64:0] Mant_roundUp_Vector_S; // for all the formats + + assign Mant_roundUp_Vector_S={7'h0,(FP16ALT_SI&&Mant_roundUp_S),2'h0,(FP16_SI&&Mant_roundUp_S),12'h0,(FP32_SI&&Mant_roundUp_S),28'h0,(FP64_SI&&Mant_roundUp_S)}; + + + assign Mant_upperRounded_D = Mant_upper_D + Mant_roundUp_Vector_S; + assign Mant_renorm_S = Mant_upperRounded_D[C_MANT_FP64+1]; + + ///////////////////////////////////////////////////////////////////////////// + // Renormalization for Rounding // + ///////////////////////////////////////////////////////////////////////////// + logic [C_MANT_FP64-1:0] Mant_res_round_D; + logic [C_EXP_FP64-1:0] Exp_res_round_D; + + + assign Mant_res_round_D = (Mant_renorm_S)?Mant_upperRounded_D[C_MANT_FP64:1]:Mant_upperRounded_D[C_MANT_FP64-1:0]; // including the process of the hidden bit + assign Exp_res_round_D = Exp_res_norm_D+Mant_renorm_S; + + ///////////////////////////////////////////////////////////////////////////// + // Output Assignments // + ///////////////////////////////////////////////////////////////////////////// + logic [C_MANT_FP64-1:0] Mant_before_format_ctl_D; + logic [C_EXP_FP64-1:0] Exp_before_format_ctl_D; + assign Mant_before_format_ctl_D = Full_precision_SI ? Mant_res_round_D : Mant_res_norm_D; + assign Exp_before_format_ctl_D = Full_precision_SI ? Exp_res_round_D : Exp_res_norm_D; + + always_comb //NaN Boxing + begin // + if(FP32_SI) + begin + Result_DO ={32'hffff_ffff,Sign_res_D,Exp_before_format_ctl_D[C_EXP_FP32-1:0],Mant_before_format_ctl_D[C_MANT_FP64-1:C_MANT_FP64-C_MANT_FP32]}; + end + else if(FP64_SI) + begin + Result_DO ={Sign_res_D,Exp_before_format_ctl_D[C_EXP_FP64-1:0],Mant_before_format_ctl_D[C_MANT_FP64-1:0]}; + end + else if(FP16_SI) + begin + Result_DO ={48'hffff_ffff_ffff,Sign_res_D,Exp_before_format_ctl_D[C_EXP_FP16-1:0],Mant_before_format_ctl_D[C_MANT_FP64-1:C_MANT_FP64-C_MANT_FP16]}; + end + else + begin + Result_DO ={48'hffff_ffff_ffff,Sign_res_D,Exp_before_format_ctl_D[C_EXP_FP16ALT-1:0],Mant_before_format_ctl_D[C_MANT_FP64-1:C_MANT_FP64-C_MANT_FP16ALT]}; + end + end + +assign In_Exact_S = (~Full_precision_SI) | Mant_rounded_S; +assign Fflags_SO = {NV_OP_S,Div_Zero_S,Exp_OF_S,Exp_UF_S,In_Exact_S}; //{NV,DZ,OF,UF,NX} + +endmodule // norm_div_sqrt_mvp
diff --git a/verilog/rtl/nrbd_nrsc_mvp.sv b/verilog/rtl/nrbd_nrsc_mvp.sv new file mode 100644 index 0000000..62bd147 --- /dev/null +++ b/verilog/rtl/nrbd_nrsc_mvp.sv
@@ -0,0 +1,104 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +//////////////////////////////////////////////////////////////////////////////// +// Company: IIS @ ETHZ - Federal Institute of Technology // +// // +// Engineers: Lei Li lile@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 10/04/2018 // +// Design Name: FPU // +// Module Name: nrbd_nrsc_mvp.sv // +// Project Name: Private FPU // +// Language: SystemVerilog // +// // +// Description: non restroring binary divisior/ square root // +// // +// Revision Date: 12/04/2018 // +// Lei Li // +// To address some requirements by Stefan and add low power // +// control for special cases // +// // +//////////////////////////////////////////////////////////////////////////////// + +import defs_div_sqrt_mvp::*; + +module nrbd_nrsc_mvp + + (//Input + input logic Clk_CI, + input logic Rst_RBI, + input logic Div_start_SI, + input logic Sqrt_start_SI, + input logic Start_SI, + input logic Kill_SI, + input logic Special_case_SBI, + input logic Special_case_dly_SBI, + input logic [C_PC-1:0] Precision_ctl_SI, + input logic [1:0] Format_sel_SI, + input logic [C_MANT_FP64:0] Mant_a_DI, + input logic [C_MANT_FP64:0] Mant_b_DI, + input logic [C_EXP_FP64:0] Exp_a_DI, + input logic [C_EXP_FP64:0] Exp_b_DI, + //output + output logic Div_enable_SO, + output logic Sqrt_enable_SO, + + output logic Full_precision_SO, + output logic FP32_SO, + output logic FP64_SO, + output logic FP16_SO, + output logic FP16ALT_SO, + output logic Ready_SO, + output logic Done_SO, + output logic [C_MANT_FP64+4:0] Mant_z_DO, + output logic [C_EXP_FP64+1:0] Exp_z_DO + ); + + + logic Div_start_dly_S,Sqrt_start_dly_S; + + +control_mvp control_U0 +( .Clk_CI (Clk_CI ), + .Rst_RBI (Rst_RBI ), + .Div_start_SI (Div_start_SI ), + .Sqrt_start_SI (Sqrt_start_SI ), + .Start_SI (Start_SI ), + .Kill_SI (Kill_SI ), + .Special_case_SBI (Special_case_SBI ), + .Special_case_dly_SBI (Special_case_dly_SBI ), + .Precision_ctl_SI (Precision_ctl_SI ), + .Format_sel_SI (Format_sel_SI ), + .Numerator_DI (Mant_a_DI ), + .Exp_num_DI (Exp_a_DI ), + .Denominator_DI (Mant_b_DI ), + .Exp_den_DI (Exp_b_DI ), + .Div_start_dly_SO (Div_start_dly_S ), + .Sqrt_start_dly_SO (Sqrt_start_dly_S ), + .Div_enable_SO (Div_enable_SO ), + .Sqrt_enable_SO (Sqrt_enable_SO ), + .Full_precision_SO (Full_precision_SO ), + .FP32_SO (FP32_SO ), + .FP64_SO (FP64_SO ), + .FP16_SO (FP16_SO ), + .FP16ALT_SO (FP16ALT_SO ), + .Ready_SO (Ready_SO ), + .Done_SO (Done_SO ), + .Mant_result_prenorm_DO (Mant_z_DO ), + .Exp_result_prenorm_DO (Exp_z_DO ) +); + + + +endmodule
diff --git a/verilog/rtl/preprocess_mvp.sv b/verilog/rtl/preprocess_mvp.sv new file mode 100644 index 0000000..9e0d25f --- /dev/null +++ b/verilog/rtl/preprocess_mvp.sv
@@ -0,0 +1,425 @@ +// Copyright 2018 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the “License”); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +//////////////////////////////////////////////////////////////////////////////// +// Company: IIS @ ETHZ - Federal Institute of Technology // +// // +// Engineers: Lei Li //lile@iis.ee.ethz.ch // +// // +// Additional contributions by: // +// // +// // +// // +// Create Date: 01/03/2018 // +// Design Name: FPU // +// Module Name: preprocess_mvp.sv // +// Project Name: Private FPU // +// Language: SystemVerilog // +// // +// Description: decode and data preparation // +// // +// Revision Date: 12/04/2018 // +// Lei Li // +// To address some requirements by Stefan and add low power // +// control for special cases // +// // +// // +//////////////////////////////////////////////////////////////////////////////// + +import defs_div_sqrt_mvp::*; + +module preprocess_mvp + ( + input logic Clk_CI, + input logic Rst_RBI, + input logic Div_start_SI, + input logic Sqrt_start_SI, + input logic Ready_SI, + //Input Operands + input logic [C_OP_FP64-1:0] Operand_a_DI, + input logic [C_OP_FP64-1:0] Operand_b_DI, + input logic [C_RM-1:0] RM_SI, //Rounding Mode + input logic [C_FS-1:0] Format_sel_SI, // Format Selection + + // to control + output logic Start_SO, + output logic [C_EXP_FP64:0] Exp_a_DO_norm, + output logic [C_EXP_FP64:0] Exp_b_DO_norm, + output logic [C_MANT_FP64:0] Mant_a_DO_norm, + output logic [C_MANT_FP64:0] Mant_b_DO_norm, + + output logic [C_RM-1:0] RM_dly_SO, + + output logic Sign_z_DO, + output logic Inf_a_SO, + output logic Inf_b_SO, + output logic Zero_a_SO, + output logic Zero_b_SO, + output logic NaN_a_SO, + output logic NaN_b_SO, + output logic SNaN_SO, + output logic Special_case_SBO, + output logic Special_case_dly_SBO + ); + + //Hidden Bits + logic Hb_a_D; + logic Hb_b_D; + + logic [C_EXP_FP64-1:0] Exp_a_D; + logic [C_EXP_FP64-1:0] Exp_b_D; + logic [C_MANT_FP64-1:0] Mant_a_NonH_D; + logic [C_MANT_FP64-1:0] Mant_b_NonH_D; + logic [C_MANT_FP64:0] Mant_a_D; + logic [C_MANT_FP64:0] Mant_b_D; + + ///////////////////////////////////////////////////////////////////////////// + // Disassemble operands + ///////////////////////////////////////////////////////////////////////////// + logic Sign_a_D,Sign_b_D; + logic Start_S; + + always_comb + begin + case(Format_sel_SI) + 2'b00: + begin + Sign_a_D = Operand_a_DI[C_OP_FP32-1]; + Sign_b_D = Operand_b_DI[C_OP_FP32-1]; + Exp_a_D = {3'h0, Operand_a_DI[C_OP_FP32-2:C_MANT_FP32]}; + Exp_b_D = {3'h0, Operand_b_DI[C_OP_FP32-2:C_MANT_FP32]}; + Mant_a_NonH_D = {Operand_a_DI[C_MANT_FP32-1:0],29'h0}; + Mant_b_NonH_D = {Operand_b_DI[C_MANT_FP32-1:0],29'h0}; + end + 2'b01: + begin + Sign_a_D = Operand_a_DI[C_OP_FP64-1]; + Sign_b_D = Operand_b_DI[C_OP_FP64-1]; + Exp_a_D = Operand_a_DI[C_OP_FP64-2:C_MANT_FP64]; + Exp_b_D = Operand_b_DI[C_OP_FP64-2:C_MANT_FP64]; + Mant_a_NonH_D = Operand_a_DI[C_MANT_FP64-1:0]; + Mant_b_NonH_D = Operand_b_DI[C_MANT_FP64-1:0]; + end + 2'b10: + begin + Sign_a_D = Operand_a_DI[C_OP_FP16-1]; + Sign_b_D = Operand_b_DI[C_OP_FP16-1]; + Exp_a_D = {6'h00, Operand_a_DI[C_OP_FP16-2:C_MANT_FP16]}; + Exp_b_D = {6'h00, Operand_b_DI[C_OP_FP16-2:C_MANT_FP16]}; + Mant_a_NonH_D = {Operand_a_DI[C_MANT_FP16-1:0],42'h0}; + Mant_b_NonH_D = {Operand_b_DI[C_MANT_FP16-1:0],42'h0}; + end + 2'b11: + begin + Sign_a_D = Operand_a_DI[C_OP_FP16ALT-1]; + Sign_b_D = Operand_b_DI[C_OP_FP16ALT-1]; + Exp_a_D = {3'h0, Operand_a_DI[C_OP_FP16ALT-2:C_MANT_FP16ALT]}; + Exp_b_D = {3'h0, Operand_b_DI[C_OP_FP16ALT-2:C_MANT_FP16ALT]}; + Mant_a_NonH_D = {Operand_a_DI[C_MANT_FP16ALT-1:0],45'h0}; + Mant_b_NonH_D = {Operand_b_DI[C_MANT_FP16ALT-1:0],45'h0}; + end + endcase + end + + + assign Mant_a_D = {Hb_a_D,Mant_a_NonH_D}; + assign Mant_b_D = {Hb_b_D,Mant_b_NonH_D}; + + assign Hb_a_D = | Exp_a_D; // hidden bit + assign Hb_b_D = | Exp_b_D; // hidden bit + + assign Start_S= Div_start_SI | Sqrt_start_SI; + + + + ///////////////////////////////////////////////////////////////////////////// + // preliminary checks for infinite/zero/NaN operands // + ///////////////////////////////////////////////////////////////////////////// + + logic Mant_a_prenorm_zero_S; + logic Mant_b_prenorm_zero_S; + + logic Exp_a_prenorm_zero_S; + logic Exp_b_prenorm_zero_S; + assign Exp_a_prenorm_zero_S = ~Hb_a_D; + assign Exp_b_prenorm_zero_S = ~Hb_b_D; + + logic Exp_a_prenorm_Inf_NaN_S; + logic Exp_b_prenorm_Inf_NaN_S; + + logic Mant_a_prenorm_QNaN_S; + logic Mant_a_prenorm_SNaN_S; + logic Mant_b_prenorm_QNaN_S; + logic Mant_b_prenorm_SNaN_S; + + assign Mant_a_prenorm_QNaN_S=Mant_a_NonH_D[C_MANT_FP64-1]&&(~(|Mant_a_NonH_D[C_MANT_FP64-2:0])); + assign Mant_a_prenorm_SNaN_S=(~Mant_a_NonH_D[C_MANT_FP64-1])&&((|Mant_a_NonH_D[C_MANT_FP64-2:0])); + assign Mant_b_prenorm_QNaN_S=Mant_b_NonH_D[C_MANT_FP64-1]&&(~(|Mant_b_NonH_D[C_MANT_FP64-2:0])); + assign Mant_b_prenorm_SNaN_S=(~Mant_b_NonH_D[C_MANT_FP64-1])&&((|Mant_b_NonH_D[C_MANT_FP64-2:0])); + + always_comb + begin + case(Format_sel_SI) + 2'b00: + begin + Mant_a_prenorm_zero_S=(Operand_a_DI[C_MANT_FP32-1:0] == C_MANT_ZERO_FP32); + Mant_b_prenorm_zero_S=(Operand_b_DI[C_MANT_FP32-1:0] == C_MANT_ZERO_FP32); + Exp_a_prenorm_Inf_NaN_S=(Operand_a_DI[C_OP_FP32-2:C_MANT_FP32] == C_EXP_INF_FP32); + Exp_b_prenorm_Inf_NaN_S=(Operand_b_DI[C_OP_FP32-2:C_MANT_FP32] == C_EXP_INF_FP32); + end + 2'b01: + begin + Mant_a_prenorm_zero_S=(Operand_a_DI[C_MANT_FP64-1:0] == C_MANT_ZERO_FP64); + Mant_b_prenorm_zero_S=(Operand_b_DI[C_MANT_FP64-1:0] == C_MANT_ZERO_FP64); + Exp_a_prenorm_Inf_NaN_S=(Operand_a_DI[C_OP_FP64-2:C_MANT_FP64] == C_EXP_INF_FP64); + Exp_b_prenorm_Inf_NaN_S=(Operand_b_DI[C_OP_FP64-2:C_MANT_FP64] == C_EXP_INF_FP64); + end + 2'b10: + begin + Mant_a_prenorm_zero_S=(Operand_a_DI[C_MANT_FP16-1:0] == C_MANT_ZERO_FP16); + Mant_b_prenorm_zero_S=(Operand_b_DI[C_MANT_FP16-1:0] == C_MANT_ZERO_FP16); + Exp_a_prenorm_Inf_NaN_S=(Operand_a_DI[C_OP_FP16-2:C_MANT_FP16] == C_EXP_INF_FP16); + Exp_b_prenorm_Inf_NaN_S=(Operand_b_DI[C_OP_FP16-2:C_MANT_FP16] == C_EXP_INF_FP16); + end + 2'b11: + begin + Mant_a_prenorm_zero_S=(Operand_a_DI[C_MANT_FP16ALT-1:0] == C_MANT_ZERO_FP16ALT); + Mant_b_prenorm_zero_S=(Operand_b_DI[C_MANT_FP16ALT-1:0] == C_MANT_ZERO_FP16ALT); + Exp_a_prenorm_Inf_NaN_S=(Operand_a_DI[C_OP_FP16ALT-2:C_MANT_FP16ALT] == C_EXP_INF_FP16ALT); + Exp_b_prenorm_Inf_NaN_S=(Operand_b_DI[C_OP_FP16ALT-2:C_MANT_FP16ALT] == C_EXP_INF_FP16ALT); + end + endcase + end + + + + + logic Zero_a_SN,Zero_a_SP; + logic Zero_b_SN,Zero_b_SP; + logic Inf_a_SN,Inf_a_SP; + logic Inf_b_SN,Inf_b_SP; + logic NaN_a_SN,NaN_a_SP; + logic NaN_b_SN,NaN_b_SP; + logic SNaN_SN,SNaN_SP; + + assign Zero_a_SN = (Start_S&&Ready_SI)?(Exp_a_prenorm_zero_S&&Mant_a_prenorm_zero_S):Zero_a_SP; + assign Zero_b_SN = (Start_S&&Ready_SI)?(Exp_b_prenorm_zero_S&&Mant_b_prenorm_zero_S):Zero_b_SP; + assign Inf_a_SN = (Start_S&&Ready_SI)?(Exp_a_prenorm_Inf_NaN_S&&Mant_a_prenorm_zero_S):Inf_a_SP; + assign Inf_b_SN = (Start_S&&Ready_SI)?(Exp_b_prenorm_Inf_NaN_S&&Mant_b_prenorm_zero_S):Inf_b_SP; + assign NaN_a_SN = (Start_S&&Ready_SI)?(Exp_a_prenorm_Inf_NaN_S&&(~Mant_a_prenorm_zero_S)):NaN_a_SP; + assign NaN_b_SN = (Start_S&&Ready_SI)?(Exp_b_prenorm_Inf_NaN_S&&(~Mant_b_prenorm_zero_S)):NaN_b_SP; + assign SNaN_SN = (Start_S&&Ready_SI) ? ((Mant_a_prenorm_SNaN_S&&NaN_a_SN) | (Mant_b_prenorm_SNaN_S&&NaN_b_SN)) : SNaN_SP; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Zero_a_SP <='0; + Zero_b_SP <='0; + Inf_a_SP <='0; + Inf_b_SP <='0; + NaN_a_SP <='0; + NaN_b_SP <='0; + SNaN_SP <= '0; + end + else + begin + Inf_a_SP <=Inf_a_SN; + Inf_b_SP <=Inf_b_SN; + Zero_a_SP <=Zero_a_SN; + Zero_b_SP <=Zero_b_SN; + NaN_a_SP <=NaN_a_SN; + NaN_b_SP <=NaN_b_SN; + SNaN_SP <= SNaN_SN; + end + end + + ///////////////////////////////////////////////////////////////////////////// + // Low power control + ///////////////////////////////////////////////////////////////////////////// + + assign Special_case_SBO=(~{(Div_start_SI)?(Zero_a_SN | Zero_b_SN | Inf_a_SN | Inf_b_SN | NaN_a_SN | NaN_b_SN): (Zero_a_SN | Inf_a_SN | NaN_a_SN | Sign_a_D) })&&(Start_S&&Ready_SI); + + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Special_case_dly_SBO <= '0; + end + else if((Start_S&&Ready_SI)) + begin + Special_case_dly_SBO <= Special_case_SBO; + end + else if(Special_case_dly_SBO) + begin + Special_case_dly_SBO <= 1'b1; + end + else + begin + Special_case_dly_SBO <= '0; + end + end + + ///////////////////////////////////////////////////////////////////////////// + // Delay sign for normalization and round // + ///////////////////////////////////////////////////////////////////////////// + + logic Sign_z_DN; + logic Sign_z_DP; + + always_comb + begin + if(Div_start_SI&&Ready_SI) + Sign_z_DN = Sign_a_D ^ Sign_b_D; + else if(Sqrt_start_SI&&Ready_SI) + Sign_z_DN = Sign_a_D; + else + Sign_z_DN = Sign_z_DP; + end + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Sign_z_DP <= '0; + end + else + begin + Sign_z_DP <= Sign_z_DN; + end + end + + logic [C_RM-1:0] RM_DN; + logic [C_RM-1:0] RM_DP; + + always_comb + begin + if(Start_S&&Ready_SI) + RM_DN = RM_SI; + else + RM_DN = RM_DP; + end + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + RM_DP <= '0; + end + else + begin + RM_DP <= RM_DN; + end + end + assign RM_dly_SO = RM_DP; + + logic [5:0] Mant_leadingOne_a, Mant_leadingOne_b; + logic Mant_zero_S_a,Mant_zero_S_b; + + lzc #( + .WIDTH ( C_MANT_FP64+1 ), + .MODE ( 1 ) + ) LOD_Ua ( + .in_i ( Mant_a_D ), + .cnt_o ( Mant_leadingOne_a ), + .empty_o ( Mant_zero_S_a ) + ); + + logic [C_MANT_FP64:0] Mant_a_norm_DN,Mant_a_norm_DP; + + assign Mant_a_norm_DN = ((Start_S&&Ready_SI))?(Mant_a_D<<(Mant_leadingOne_a)):Mant_a_norm_DP; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Mant_a_norm_DP <= '0; + end + else + begin + Mant_a_norm_DP<=Mant_a_norm_DN; + end + end + + logic [C_EXP_FP64:0] Exp_a_norm_DN,Exp_a_norm_DP; + assign Exp_a_norm_DN = ((Start_S&&Ready_SI))?(Exp_a_D-Mant_leadingOne_a+(|Mant_leadingOne_a)):Exp_a_norm_DP; //Covering the process of denormal numbers + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Exp_a_norm_DP <= '0; + end + else + begin + Exp_a_norm_DP<=Exp_a_norm_DN; + end + end + + lzc #( + .WIDTH ( C_MANT_FP64+1 ), + .MODE ( 1 ) + ) LOD_Ub ( + .in_i ( Mant_b_D ), + .cnt_o ( Mant_leadingOne_b ), + .empty_o ( Mant_zero_S_b ) + ); + + + logic [C_MANT_FP64:0] Mant_b_norm_DN,Mant_b_norm_DP; + + assign Mant_b_norm_DN = ((Start_S&&Ready_SI))?(Mant_b_D<<(Mant_leadingOne_b)):Mant_b_norm_DP; + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Mant_b_norm_DP <= '0; + end + else + begin + Mant_b_norm_DP<=Mant_b_norm_DN; + end + end + + logic [C_EXP_FP64:0] Exp_b_norm_DN,Exp_b_norm_DP; + assign Exp_b_norm_DN = ((Start_S&&Ready_SI))?(Exp_b_D-Mant_leadingOne_b+(|Mant_leadingOne_b)):Exp_b_norm_DP; //Covering the process of denormal numbers + + always_ff @(posedge Clk_CI, negedge Rst_RBI) + begin + if(~Rst_RBI) + begin + Exp_b_norm_DP <= '0; + end + else + begin + Exp_b_norm_DP<=Exp_b_norm_DN; + end + end + + ///////////////////////////////////////////////////////////////////////////// + // Output assignments // + ///////////////////////////////////////////////////////////////////////////// + + assign Start_SO=Start_S; + assign Exp_a_DO_norm=Exp_a_norm_DP; + assign Exp_b_DO_norm=Exp_b_norm_DP; + assign Mant_a_DO_norm=Mant_a_norm_DP; + assign Mant_b_DO_norm=Mant_b_norm_DP; + assign Sign_z_DO=Sign_z_DP; + assign Inf_a_SO=Inf_a_SP; + assign Inf_b_SO=Inf_b_SP; + assign Zero_a_SO=Zero_a_SP; + assign Zero_b_SO=Zero_b_SP; + assign NaN_a_SO=NaN_a_SP; + assign NaN_b_SO=NaN_b_SP; + assign SNaN_SO=SNaN_SP; + +endmodule
diff --git a/verilog/rtl/prim_arbiter_ppc.sv b/verilog/rtl/prim_arbiter_ppc.sv new file mode 100644 index 0000000..e379f8a --- /dev/null +++ b/verilog/rtl/prim_arbiter_ppc.sv
@@ -0,0 +1,127 @@ + +// +// N:1 arbiter module +// +// Verilog parameter +// N: Number of request ports +// DW: Data width +// DataPort: Set to 1 to enable the data port. Otherwise that port will be ignored. +// EnReqStabA: Checks whether requests remain asserted until granted +// +// This is the original implementation of the arbiter which relies on parallel prefix computing +// optimization to optimize the request / arbiter tree. Not all synthesis tools may support this. +// +// Note that the currently winning request is held if the data sink is not ready. This behavior is +// required by some interconnect protocols (AXI, TL). The module contains an assertion that checks +// this behavior. +// +// Also, this module contains a request stability assertion that checks that requests stay asserted +// until they have been served. This assertion can be optionally disabled by setting EnReqStabA to +// zero. This is a non-functional parameter and does not affect the designs behavior. +// +// See also: prim_arbiter_tree + + + +module prim_arbiter_ppc #( + parameter int unsigned N = 8, + parameter int unsigned DW = 32, + + // Configurations + // EnDataPort: {0, 1}, if 0, input data will be ignored + parameter bit EnDataPort = 1, + + // Non-functional parameter to switch on the request stability assertion + parameter bit EnReqStabA = 1, + + // Derived parameters + localparam int IdxW = $clog2(N) +) ( + input clk_i, + input rst_ni, + + input [ N-1:0] req_i, + input [DW-1:0] data_i [N], + output logic [ N-1:0] gnt_o, + output logic [IdxW-1:0] idx_o, + + output logic valid_o, + output logic [DW-1:0] data_o, + input ready_i +); + + + // this case is basically just a bypass + if (N == 1) begin : gen_degenerate_case + + assign valid_o = req_i[0]; + assign data_o = data_i[0]; + assign gnt_o[0] = valid_o & ready_i; + assign idx_o = '0; + + end else begin : gen_normal_case + + logic [N-1:0] masked_req; + logic [N-1:0] ppc_out; + logic [N-1:0] arb_req; + logic [N-1:0] mask, mask_next; + logic [N-1:0] winner; + + assign masked_req = mask & req_i; + assign arb_req = (|masked_req) ? masked_req : req_i; + + // PPC + // Even below code looks O(n) but DC optimizes it to O(log(N)) + // Using Parallel Prefix Computation + always_comb begin + ppc_out[0] = arb_req[0]; + for (int i = 1 ; i < N ; i++) begin + ppc_out[i] = ppc_out[i-1] | arb_req[i]; + end + end + + // Grant Generation: Leading-One detector + assign winner = ppc_out ^ {ppc_out[N-2:0], 1'b0}; + assign gnt_o = (ready_i) ? winner : '0; + + assign valid_o = |req_i; + // Mask Generation + assign mask_next = {ppc_out[N-2:0], 1'b0}; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + mask <= '0; + end else if (valid_o && ready_i) begin + // Latch only when requests accepted + mask <= mask_next; + end else if (valid_o && !ready_i) begin + // Downstream isn't yet ready so, keep current request alive. (First come first serve) + mask <= ppc_out; + end + end + + if (EnDataPort == 1) begin: gen_datapath + always_comb begin + data_o = '0; + for (int i = 0 ; i < N ; i++) begin + if (winner[i]) begin + data_o = data_i[i]; + end + end + end + end else begin: gen_nodatapath + assign data_o = '1; + // TODO: waive data_i from NOT_READ error + end + + always_comb begin + idx_o = '0; + for (int unsigned i = 0 ; i < N ; i++) begin + if (winner[i]) begin + idx_o = i[IdxW-1:0]; + end + end + end + end + + +endmodule : prim_arbiter_ppc
diff --git a/verilog/rtl/prim_clock_gating.sv b/verilog/rtl/prim_clock_gating.sv new file mode 100644 index 0000000..177d2c0 --- /dev/null +++ b/verilog/rtl/prim_clock_gating.sv
@@ -0,0 +1,12 @@ + + +module prim_clock_gating ( + input clk_i, + input en_i, + input test_en_i, + output logic clk_o +); + +sky130_fd_sc_hd__dlclkp_1 CG( .CLK(clk_i), .GCLK(clk_o), .GATE(en_i | test_en_i)); + +endmodule
diff --git a/verilog/rtl/prim_filter_ctr.sv b/verilog/rtl/prim_filter_ctr.sv new file mode 100644 index 0000000..ec635b8 --- /dev/null +++ b/verilog/rtl/prim_filter_ctr.sv
@@ -0,0 +1,63 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Primitive counter-based input filter, with enable. +// Configurable number of cycles. Cheaper version of filter for +// large values of #Cycles +// +// when in reset, stored value is zero +// when enable is false, output is input +// when enable is true, output is stored value, +// new input must be opposite value from stored value for +// #Cycles before switching to new value. + +module prim_filter_ctr #(parameter int unsigned Cycles = 4) ( + input clk_i, + input rst_ni, + input enable_i, + input filter_i, + output filter_o +); + + localparam int unsigned CTR_WIDTH = $clog2(Cycles); + localparam logic [CTR_WIDTH-1:0] CYCLESM1 = (CTR_WIDTH)'(Cycles-1); + + logic [CTR_WIDTH-1:0] diff_ctr_q, diff_ctr_d; + logic filter_q, stored_value_q, update_stored_value; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + filter_q <= 1'b0; + end else begin + filter_q <= filter_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + stored_value_q <= 1'b0; + end else if (update_stored_value) begin + stored_value_q <= filter_i; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + diff_ctr_q <= {CTR_WIDTH{1'b0}}; + end else begin + diff_ctr_q <= diff_ctr_d; + end + end + + // always look for differences, even if not filter enabled + assign diff_ctr_d = + (filter_i != filter_q) ? '0 : // restart + (diff_ctr_q == CYCLESM1) ? CYCLESM1 : // saturate + (diff_ctr_q + 1'b1); // count up + assign update_stored_value = (diff_ctr_d == CYCLESM1); + + assign filter_o = enable_i ? stored_value_q : filter_i; + +endmodule +
diff --git a/verilog/rtl/prim_generic_clock_inv.sv b/verilog/rtl/prim_generic_clock_inv.sv new file mode 100644 index 0000000..ae8f6b6 --- /dev/null +++ b/verilog/rtl/prim_generic_clock_inv.sv
@@ -0,0 +1,29 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Clock inverter +// Varies on the process + +module prim_generic_clock_inv #( + parameter bit HasScanMode = 1'b1 +) ( + input clk_i, + input scanmode_i, + output logic clk_no // Inverted +); + + if (HasScanMode) begin : gen_scan + prim_generic_clock_mux2 i_dft_tck_mux ( + .clk0_i ( ~clk_i ), + .clk1_i ( clk_i ), // bypass the inverted clock for testing + .sel_i ( scanmode_i ), + .clk_o ( clk_no ) + ); + end else begin : gen_noscan + logic unused_scanmode; + assign unused_scanmode = scanmode_i; + assign clk_no = ~clk_i; + end + +endmodule : prim_generic_clock_inv
diff --git a/verilog/rtl/prim_generic_clock_mux2.sv b/verilog/rtl/prim_generic_clock_mux2.sv new file mode 100644 index 0000000..8f296e6 --- /dev/null +++ b/verilog/rtl/prim_generic_clock_mux2.sv
@@ -0,0 +1,22 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + + + +module prim_generic_clock_mux2 #( + parameter bit NoFpgaBufG = 1'b0 // this parameter serves no function in the generic model +) ( + input clk0_i, + input clk1_i, + input sel_i, + output logic clk_o +); + + assign clk_o = (sel_i) ? clk1_i : clk0_i; + + // make sure sel is never X (including during reset) + // need to use ##1 as this could break with inverted clocks that + // start with a rising edge at the beginning of the simulation. + +endmodule : prim_generic_clock_mux2
diff --git a/verilog/rtl/prim_generic_flop.sv b/verilog/rtl/prim_generic_flop.sv new file mode 100644 index 0000000..8eacf01 --- /dev/null +++ b/verilog/rtl/prim_generic_flop.sv
@@ -0,0 +1,26 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// `include "prim_assert.sv" + +module prim_generic_flop # ( + parameter int Width = 1, + localparam int WidthSubOne = Width-1, + parameter logic [WidthSubOne:0] ResetValue = 0 +) ( + input clk_i, + input rst_ni, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q_o <= ResetValue; + end else begin + q_o <= d_i; + end + end + +endmodule // prim_generic_flop
diff --git a/verilog/rtl/prim_generic_flop_2sync.sv b/verilog/rtl/prim_generic_flop_2sync.sv new file mode 100644 index 0000000..fdd1358 --- /dev/null +++ b/verilog/rtl/prim_generic_flop_2sync.sv
@@ -0,0 +1,43 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Generic double-synchronizer flop +// This may need to be moved to prim_generic if libraries have a specific cell +// for synchronization + +module prim_generic_flop_2sync #( + parameter int Width = 16, + localparam int WidthSubOne = Width-1, // temp work around #2679 + parameter logic [WidthSubOne:0] ResetValue = '0 +) ( + input clk_i, // receive clock + input rst_ni, + input [Width-1:0] d_i, + output logic [Width-1:0] q_o +); + + logic [Width-1:0] intq; + + prim_generic_flop #( + .Width(Width), + .ResetValue(ResetValue) + ) u_sync_1 ( + .clk_i, + .rst_ni, + .d_i, + .q_o(intq) + ); + + prim_generic_flop #( + .Width(Width), + .ResetValue(ResetValue) + ) u_sync_2 ( + .clk_i, + .rst_ni, + .d_i(intq), + .q_o + ); + + +endmodule
diff --git a/verilog/rtl/prim_intr_hw.sv b/verilog/rtl/prim_intr_hw.sv new file mode 100644 index 0000000..c84e389 --- /dev/null +++ b/verilog/rtl/prim_intr_hw.sv
@@ -0,0 +1,51 @@ + + +module prim_intr_hw # ( + parameter int unsigned Width = 1, + parameter bit FlopOutput = 1 +) ( + // event + input clk_i, + input rst_ni, + input [Width-1:0] event_intr_i, + + // register interface + input [Width-1:0] reg2hw_intr_enable_q_i, + input [Width-1:0] reg2hw_intr_test_q_i, + input reg2hw_intr_test_qe_i, + input [Width-1:0] reg2hw_intr_state_q_i, + output hw2reg_intr_state_de_o, + output [Width-1:0] hw2reg_intr_state_d_o, + + // outgoing interrupt + output logic [Width-1:0] intr_o +); + + logic [Width-1:0] new_event; + assign new_event = + (({Width{reg2hw_intr_test_qe_i}} & reg2hw_intr_test_q_i) | event_intr_i); + assign hw2reg_intr_state_de_o = |new_event; + // for scalar interrupts, this resolves to '1' with new event + // for vector interrupts, new events are OR'd in to existing interrupt state + assign hw2reg_intr_state_d_o = new_event | reg2hw_intr_state_q_i; + + if (FlopOutput == 1) begin : gen_flop_intr_output + // flop the interrupt output + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + intr_o <= 1'b0; + end else begin + intr_o <= reg2hw_intr_state_q_i & reg2hw_intr_enable_q_i; + end + end + + end else begin : gen_intr_passthrough_output + logic unused_clk; + logic unused_rst_n; + assign unused_clk = clk_i; + assign unused_rst_n = rst_ni; + assign intr_o = reg2hw_intr_state_q_i & reg2hw_intr_enable_q_i; + end + + +endmodule
diff --git a/verilog/rtl/prim_pkg.sv b/verilog/rtl/prim_pkg.sv new file mode 100644 index 0000000..ebe38d1 --- /dev/null +++ b/verilog/rtl/prim_pkg.sv
@@ -0,0 +1,18 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Constants for use in primitives +// +// This file is a stop-gap until the DV file list is generated by FuseSoC. +// Its contents are taken from the file which would be generated by FuseSoC. +// https://github.com/lowRISC/ibex/issues/893 + +package prim_pkg; + + // Implementation target specialization + typedef enum integer { + ImplGeneric, + ImplXilinx + } impl_e; +endpackage : prim_pkg \ No newline at end of file
diff --git a/verilog/rtl/prim_subreg.sv b/verilog/rtl/prim_subreg.sv new file mode 100644 index 0000000..33d4290 --- /dev/null +++ b/verilog/rtl/prim_subreg.sv
@@ -0,0 +1,60 @@ + + +module prim_subreg #( + parameter int DW = 32 , + parameter SWACCESS = "RW", // {RW, RO, WO, W1C, W1S, W0C, RC} + parameter logic [DW-1:0] RESVAL = '0 // Reset value +) ( + input clk_i, + input rst_ni, + + // From SW: valid for RW, WO, W1C, W1S, W0C, RC + // In case of RC, Top connects Read Pulse to we + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO + input de, + input [DW-1:0] d, + + // output to HW and Reg Read + output logic qe, + output logic [DW-1:0] q, + output logic [DW-1:0] qs +); + + logic wr_en; + logic [DW-1:0] wr_data; + + prim_subreg_arb #( + .DW ( DW ), + .SWACCESS ( SWACCESS ) + ) wr_en_data_arb ( + .we, + .wd, + .de, + .d, + .q, + .wr_en, + .wr_data + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + qe <= 1'b0; + end else begin + qe <= we; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q <= RESVAL; + end else if (wr_en) begin + q <= wr_data; + end + end + + assign qs = q; + +endmodule
diff --git a/verilog/rtl/prim_subreg_arb.sv b/verilog/rtl/prim_subreg_arb.sv new file mode 100644 index 0000000..adc144f --- /dev/null +++ b/verilog/rtl/prim_subreg_arb.sv
@@ -0,0 +1,75 @@ + + +module prim_subreg_arb #( + parameter int DW = 32 , + parameter SWACCESS = "RW" // {RW, RO, WO, W1C, W1S, W0C, RC} +) ( + // From SW: valid for RW, WO, W1C, W1S, W0C, RC. + // In case of RC, top connects read pulse to we. + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO. + input de, + input [DW-1:0] d, + + // From register: actual reg value. + input [DW-1:0] q, + + // To register: actual write enable and write data. + output logic wr_en, + output logic [DW-1:0] wr_data +); + + if ((SWACCESS == "RW") || (SWACCESS == "WO")) begin : gen_w + assign wr_en = we | de; + assign wr_data = (we == 1'b1) ? wd : d; // SW higher priority + // Unused q - Prevent lint errors. + logic [DW-1:0] unused_q; + assign unused_q = q; + end else if (SWACCESS == "RO") begin : gen_ro + assign wr_en = de; + assign wr_data = d; + // Unused we, wd, q - Prevent lint errors. + logic unused_we; + logic [DW-1:0] unused_wd; + logic [DW-1:0] unused_q; + assign unused_we = we; + assign unused_wd = wd; + assign unused_q = q; + end else if (SWACCESS == "W1S") begin : gen_w1s + // If SWACCESS is W1S, then assume hw tries to clear. + // So, give a chance HW to clear when SW tries to set. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de; + assign wr_data = (de ? d : q) | (we ? wd : '0); + end else if (SWACCESS == "W1C") begin : gen_w1c + // If SWACCESS is W1C, then assume hw tries to set. + // So, give a chance HW to set when SW tries to clear. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de; + assign wr_data = (de ? d : q) & (we ? ~wd : '1); + end else if (SWACCESS == "W0C") begin : gen_w0c + assign wr_en = we | de; + assign wr_data = (de ? d : q) & (we ? wd : '1); + end else if (SWACCESS == "RC") begin : gen_rc + // This swtype is not recommended but exists for compatibility. + // WARN: we signal is actually read signal not write enable. + assign wr_en = we | de; + assign wr_data = (de ? d : q) & (we ? '0 : '1); + // Unused wd - Prevent lint errors. + logic [DW-1:0] unused_wd; + assign unused_wd = wd; + end else begin : gen_hw + assign wr_en = de; + assign wr_data = d; + // Unused we, wd, q - Prevent lint errors. + logic unused_we; + logic [DW-1:0] unused_wd; + logic [DW-1:0] unused_q; + assign unused_we = we; + assign unused_wd = wd; + assign unused_q = q; + end + +endmodule
diff --git a/verilog/rtl/prim_subreg_ext.sv b/verilog/rtl/prim_subreg_ext.sv new file mode 100644 index 0000000..00d7e9c --- /dev/null +++ b/verilog/rtl/prim_subreg_ext.sv
@@ -0,0 +1,24 @@ + + +module prim_subreg_ext #( + parameter int unsigned DW = 32 +) ( + input re, + input we, + input [DW-1:0] wd, + + input [DW-1:0] d, + + // output to HW and Reg Read + output logic qe, + output logic qre, + output logic [DW-1:0] q, + output logic [DW-1:0] qs +); + + assign qs = d; + assign q = wd; + assign qe = we; + assign qre = re; + +endmodule
diff --git a/verilog/rtl/prim_util_pkg.sv b/verilog/rtl/prim_util_pkg.sv new file mode 100644 index 0000000..54d8a37 --- /dev/null +++ b/verilog/rtl/prim_util_pkg.sv
@@ -0,0 +1,89 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + + +/** + * Utility functions + */ +package prim_util_pkg; + /** + * Math function: $clog2 as specified in Verilog-2005 + * + * Do not use this function if $clog2() is available. + * + * clog2 = 0 for value == 0 + * ceil(log2(value)) for value >= 1 + * + * This implementation is a synthesizable variant of the $clog2 function as + * specified in the Verilog-2005 standard (IEEE 1364-2005). + * + * To quote the standard: + * The system function $clog2 shall return the ceiling of the log + * base 2 of the argument (the log rounded up to an integer + * value). The argument can be an integer or an arbitrary sized + * vector value. The argument shall be treated as an unsigned + * value, and an argument value of 0 shall produce a result of 0. + */ + function automatic integer _clog2(integer value); + integer result; + // Use an intermediate value to avoid assigning to an input port, which produces a warning in + // Synopsys DC. + integer v = value; + v = v - 1; + for (result = 0; v > 0; result++) begin + v = v >> 1; + end + return result; + endfunction + + + /** + * Math function: Number of bits needed to address |value| items. + * + * 0 for value == 0 + * vbits = 1 for value == 1 + * ceil(log2(value)) for value > 1 + * + * + * The primary use case for this function is the definition of registers/arrays + * which are wide enough to contain |value| items. + * + * This function identical to $clog2() for all input values except the value 1; + * it could be considered an "enhanced" $clog2() function. + * + * + * Example 1: + * parameter Items = 1; + * localparam ItemsWidth = vbits(Items); // 1 + * logic [ItemsWidth-1:0] item_register; // items_register is now [0:0] + * + * Example 2: + * parameter Items = 64; + * localparam ItemsWidth = vbits(Items); // 6 + * logic [ItemsWidth-1:0] item_register; // items_register is now [5:0] + * + * Note: If you want to store the number "value" inside a register, you need + * a register with size vbits(value + 1), since you also need to store + * the number 0. + * + * Example 3: + * logic [vbits(64)-1:0] store_64_logic_values; // width is [5:0] + * logic [vbits(64 + 1)-1:0] store_number_64; // width is [6:0] + */ + function automatic integer vbits(integer value); +`ifdef XCELIUM + // The use of system functions was not allowed here in Verilog-2001, but is + // valid since (System)Verilog-2005, which is also when $clog2() first + // appeared. + // Xcelium < 19.10 does not yet support the use of $clog2() here, fall back + // to an implementation without a system function. Remove this workaround + // if we require a newer Xcelium version. + // See #2579 and #2597. + return (value == 1) ? 1 : _clog2(value); +`else + return (value == 1) ? 1 : $clog2(value); +`endif + endfunction + +endpackage \ No newline at end of file
diff --git a/verilog/rtl/pwm_top.sv b/verilog/rtl/pwm_top.sv new file mode 100644 index 0000000..b159a93 --- /dev/null +++ b/verilog/rtl/pwm_top.sv
@@ -0,0 +1,75 @@ + +module pwm_top ( + + input clk_i, + input rst_ni, + + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + + output pwm_o, + output pwm_o_2, + output pwm1_oe, + output pwm2_oe + +); + + +localparam int AW = 8; +localparam int DW = 32; +localparam int DBW = DW/8; + +logic re; +logic we; +logic [7:0] addr; +logic [31:0] wdata; +logic [3:0] be; +logic [31:0] rdata; +logic err; + +//assign err = '0; + +PWM pwm_core( + +.clk_i (clk_i), +.rst_ni (rst_ni), + +.re_i (re), +.we_i (we), +.addr_i (addr), +.wdata_i (wdata), +.be_i (be), +.rdata_o (rdata), +//.error_o (err), + +.i_extclk ('0), +.i_DC ('0), +.i_valid_DC ('0), +.o_pwm (pwm_o), +.o_pwm_2 (pwm_o_2), +.oe_pwm1 (pwm1_oe), +.oe_pwm2 (pwm2_oe) + +); + +tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW) +) u_reg_if ( + .clk_i, + .rst_ni, + + .tl_i (tl_i), + .tl_o (tl_o), + + .we_o (we), + .re_o (re), + .addr_o (addr), + .wdata_o (wdata), + .be_o (be), + .rdata_i (rdata), + .error_i (1'b0) +); + +endmodule
diff --git a/verilog/rtl/rr_arb_tree.sv b/verilog/rtl/rr_arb_tree.sv new file mode 100644 index 0000000..9013a64 --- /dev/null +++ b/verilog/rtl/rr_arb_tree.sv
@@ -0,0 +1,343 @@ +// Copyright 2019 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich +// Wolfgang Roenninger <wroennin@iis.ee.ethz.ch>, ETH Zurich +// Date: 02.04.2019 +// Description: logarithmic arbitration tree with round robin arbitration scheme. + +/// The rr_arb_tree employs non-starving round robin-arbitration - i.e., the priorities +/// rotate each cycle. +/// +/// ## Fair vs. unfair Arbitration +/// +/// This refers to fair throughput distribution when not all inputs have active requests. +/// This module has an internal state `rr_q` which defines the highest priority input. (When +/// `ExtPrio` is `1'b1` this state is provided from the outside.) The arbitration tree will +/// choose the input with the same index as currently defined by the state if it has an active +/// request. Otherwise a *random* other active input is selected. The parameter `FairArb` is used +/// to distinguish between two methods of calculating the next state. +/// * `1'b0`: The next state is calculated by advancing the current state by one. This leads to the +/// state being calculated without the context of the active request. Leading to an +/// unfair throughput distribution if not all inputs have active requests. +/// * `1'b1`: The next state jumps to the next unserved request with higher index. +/// This is achieved by using two trailing-zero-counters (`lzc`). The upper has the masked +/// `req_i` signal with all indices which will have a higher priority in the next state. +/// The trailing zero count defines the input index with the next highest priority after +/// the current one is served. When the upper is empty the lower `lzc` provides the +/// wrapped index if there are outstanding requests with lower or same priority. +/// The implication of throughput fairness on the module timing are: +/// * The trailing zero counter (`lzc`) has a loglog relation of input to output timing. This means +/// that in this module the input to register path scales with Log(Log(`NumIn`)). +/// * The `rr_arb_tree` data multiplexing scales with Log(`NumIn`). This means that the input to output +/// timing path of this module also scales scales with Log(`NumIn`). +/// This implies that in this module the input to output path is always longer than the input to +/// register path. As the output data usually also terminates in a register the parameter `FairArb` +/// only has implications on the area. When it is `1'b0` a static plus one adder is instantiated. +/// If it is `1'b1` two `lzc`, a masking logic stage and a two input multiplexer are instantiated. +/// However these are small in respect of the data multiplexers needed, as the width of the `req_i` +/// signal is usually less as than `DataWidth`. +module rr_arb_tree #( + /// Number of inputs to be arbitrated. + parameter int unsigned NumIn = 64, + /// Data width of the payload in bits. Not needed if `DataType` is overwritten. + parameter int unsigned DataWidth = 32, + /// Data type of the payload, can be overwritten with custom type. Only use of `DataWidth`. + parameter type DataType = logic [DataWidth-1:0], + /// The `ExtPrio` option allows to override the internal round robin counter via the + /// `rr_i` signal. This can be useful in case multiple arbiters need to have + /// rotating priorities that are operating in lock-step. If static priority arbitration + /// is needed, just connect `rr_i` to '0. + /// + /// Set to 1'b1 to enable. + parameter bit ExtPrio = 1'b0, + /// If `AxiVldRdy` is set, the req/gnt signals are compliant with the AXI style vld/rdy + /// handshake. Namely, upstream vld (req) must not depend on rdy (gnt), as it can be deasserted + /// again even though vld is asserted. Enabling `AxiVldRdy` leads to a reduction of arbiter + /// delay and area. + /// + /// Set to `1'b1` to treat req/gnt as vld/rdy. + parameter bit AxiVldRdy = 1'b0, + /// The `LockIn` option prevents the arbiter from changing the arbitration + /// decision when the arbiter is disabled. I.e., the index of the first request + /// that wins the arbitration will be locked in case the destination is not + /// able to grant the request in the same cycle. + /// + /// Set to `1'b1` to enable. + parameter bit LockIn = 1'b0, + /// When set, ensures that throughput gets distributed evenly between all inputs. + /// + /// Set to `1'b0` to disable. + parameter bit FairArb = 1'b1, + /// Dependent parameter, do **not** overwrite. + /// Width of the arbitration priority signal and the arbitrated index. + parameter int unsigned IdxWidth = (NumIn > 32'd1) ? unsigned'($clog2(NumIn)) : 32'd1, + /// Dependent parameter, do **not** overwrite. + /// Type for defining the arbitration priority and arbitrated index signal. + parameter type idx_t = logic [IdxWidth-1:0] +) ( + /// clk_i, positive edge triggered. + input logic clk_i, + /// Asynchronous rst_ni, active low. + input logic rst_ni, + /// Clears the arbiter state. Only used if `ExtPrio` is `1'b0` or `LockIn` is `1'b1`. + input logic flush_i, + /// External round-robin priority. Only used if `ExtPrio` is `1'b1.` + input idx_t rr_i, + /// Input requests arbitration. + input logic [NumIn-1:0] req_i, + /* verilator lint_off UNOPTFLAT */ + /// Input request is granted. + output logic [NumIn-1:0] gnt_o, + /* verilator lint_on UNOPTFLAT */ + /// Input data for arbitration. + input DataType [NumIn-1:0] data_i, + /// Output request is valid. + output logic req_o, + /// Output request is granted. + input logic gnt_i, + /// Output data. + output DataType data_o, + /// Index from which input the data came from. + output idx_t idx_o +); + + // pragma translate_off + `ifndef VERILATOR + // Default SVA rst_ni + default disable iff (!rst_ni || flush_i); + `endif + // pragma translate_on + + // just pass through in this corner case + if (NumIn == unsigned'(1)) begin : gen_pass_through + assign req_o = req_i[0]; + assign gnt_o[0] = gnt_i; + assign data_o = data_i[0]; + assign idx_o = '0; + // non-degenerate cases + end else begin : gen_arbiter + localparam int unsigned NumLevels = unsigned'($clog2(NumIn)); + + /* verilator lint_off UNOPTFLAT */ + idx_t [2**NumLevels-2:0] index_nodes; // used to propagate the indices + DataType [2**NumLevels-2:0] data_nodes; // used to propagate the data + logic [2**NumLevels-2:0] gnt_nodes; // used to propagate the grant to masters + logic [2**NumLevels-2:0] req_nodes; // used to propagate the requests to slave + /* lint_off */ + idx_t rr_q; + logic [NumIn-1:0] req_d; + + // the final arbitration decision can be taken from the root of the tree + assign req_o = req_nodes[0]; + assign data_o = data_nodes[0]; + assign idx_o = index_nodes[0]; + + if (ExtPrio) begin : gen_ext_rr + assign rr_q = rr_i; + assign req_d = req_i; + end else begin : gen_int_rr + idx_t rr_d; + + // lock arbiter decision in case we got at least one req and no acknowledge + if (LockIn) begin : gen_lock + logic lock_d, lock_q; + logic [NumIn-1:0] req_q; + + assign lock_d = req_o & ~gnt_i; + assign req_d = (lock_q) ? req_q : req_i; + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_lock_reg + if (!rst_ni) begin + lock_q <= '0; + end else begin + if (flush_i) begin + lock_q <= '0; + end else begin + lock_q <= lock_d; + end + end + end + + // pragma translate_off + `ifndef VERILATOR + lock: assert property( + @(posedge clk_i) LockIn |-> req_o && !gnt_i |=> idx_o == $past(idx_o)) else + $fatal (1, "Lock implies same arbiter decision in next cycle if output is not \ + ready."); + + logic [NumIn-1:0] req_tmp; + assign req_tmp = req_q & req_i; + lock_req: assume property( + @(posedge clk_i) LockIn |-> lock_d |=> req_tmp == req_q) else + $fatal (1, "It is disallowed to deassert unserved request signals when LockIn is \ + enabled."); + `endif + // pragma translate_on + + always_ff @(posedge clk_i or negedge rst_ni) begin : p_req_regs + if (!rst_ni) begin + req_q <= '0; + end else begin + if (flush_i) begin + req_q <= '0; + end else begin + req_q <= req_d; + end + end + end + end else begin : gen_no_lock + assign req_d = req_i; + end + + if (FairArb) begin : gen_fair_arb + logic [NumIn-1:0] upper_mask, lower_mask; + idx_t upper_idx, lower_idx, next_idx; + logic upper_empty, lower_empty; + + for (genvar i = 0; i < NumIn; i++) begin : gen_mask + assign upper_mask[i] = (i > rr_q) ? req_d[i] : 1'b0; + assign lower_mask[i] = (i <= rr_q) ? req_d[i] : 1'b0; + end + + lzc #( + .WIDTH ( NumIn ), + .MODE ( 1'b0 ) + ) i_lzc_upper ( + .in_i ( upper_mask ), + .cnt_o ( upper_idx ), + .empty_o ( upper_empty ) + ); + + lzc #( + .WIDTH ( NumIn ), + .MODE ( 1'b0 ) + ) i_lzc_lower ( + .in_i ( lower_mask ), + .cnt_o ( lower_idx ), + .empty_o ( /*unused*/ ) + ); + + assign next_idx = upper_empty ? lower_idx : upper_idx; + assign rr_d = (gnt_i && req_o) ? next_idx : rr_q; + + end else begin : gen_unfair_arb + assign rr_d = (gnt_i && req_o) ? ((rr_q == idx_t'(NumIn-1)) ? '0 : rr_q + 1'b1) : rr_q; + end + + // this holds the highest priority + always_ff @(posedge clk_i or negedge rst_ni) begin : p_rr_regs + if (!rst_ni) begin + rr_q <= '0; + end else begin + if (flush_i) begin + rr_q <= '0; + end else begin + rr_q <= rr_d; + end + end + end + end + + assign gnt_nodes[0] = gnt_i; + + // arbiter tree + for (genvar level = 0; unsigned'(level) < NumLevels; level++) begin : gen_levels + for (genvar l = 0; l < 2**level; l++) begin : gen_level + // local select signal + logic sel; + // index calcs + localparam int unsigned Idx0 = 2**level-1+l;// current node + localparam int unsigned Idx1 = 2**(level+1)-1+l*2; + ////////////////////////////////////////////////////////////// + // uppermost level where data is fed in from the inputs + if (unsigned'(level) == NumLevels-1) begin : gen_first_level + // if two successive indices are still in the vector... + if (unsigned'(l) * 2 < NumIn-1) begin : gen_reduce + assign req_nodes[Idx0] = req_d[l*2] | req_d[l*2+1]; + + // arbitration: round robin + assign sel = ~req_d[l*2] | req_d[l*2+1] & rr_q[NumLevels-1-level]; + + assign index_nodes[Idx0] = idx_t'(sel); + assign data_nodes[Idx0] = (sel) ? data_i[l*2+1] : data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]) & ~sel; + assign gnt_o[l*2+1] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2+1]) & sel; + end + // if only the first index is still in the vector... + if (unsigned'(l) * 2 == NumIn-1) begin : gen_first + assign req_nodes[Idx0] = req_d[l*2]; + assign index_nodes[Idx0] = '0;// always zero in this case + assign data_nodes[Idx0] = data_i[l*2]; + assign gnt_o[l*2] = gnt_nodes[Idx0] & (AxiVldRdy | req_d[l*2]); + end + // if index is out of range, fill up with zeros (will get pruned) + if (unsigned'(l) * 2 > NumIn-1) begin : gen_out_of_range + assign req_nodes[Idx0] = 1'b0; + assign index_nodes[Idx0] = idx_t'('0); + assign data_nodes[Idx0] = DataType'('0); + end + ////////////////////////////////////////////////////////////// + // general case for other levels within the tree + end else begin : gen_other_levels + assign req_nodes[Idx0] = req_nodes[Idx1] | req_nodes[Idx1+1]; + + // arbitration: round robin + assign sel = ~req_nodes[Idx1] | req_nodes[Idx1+1] & rr_q[NumLevels-1-level]; + + assign index_nodes[Idx0] = (sel) ? + idx_t'({1'b1, index_nodes[Idx1+1][NumLevels-unsigned'(level)-2:0]}) : + idx_t'({1'b0, index_nodes[Idx1][NumLevels-unsigned'(level)-2:0]}); + + assign data_nodes[Idx0] = (sel) ? data_nodes[Idx1+1] : data_nodes[Idx1]; + assign gnt_nodes[Idx1] = gnt_nodes[Idx0] & ~sel; + assign gnt_nodes[Idx1+1] = gnt_nodes[Idx0] & sel; + end + ////////////////////////////////////////////////////////////// + end + end + + // pragma translate_off + `ifndef VERILATOR + initial begin : p_assert + assert(NumIn) + else $fatal(1, "Input must be at least one element wide."); + assert(!(LockIn && ExtPrio)) + else $fatal(1,"Cannot use LockIn feature together with external ExtPrio."); + end + + hot_one : assert property( + @(posedge clk_i) $onehot0(gnt_o)) + else $fatal (1, "Grant signal must be hot1 or zero."); + + gnt0 : assert property( + @(posedge clk_i) |gnt_o |-> gnt_i) + else $fatal (1, "Grant out implies grant in."); + + gnt1 : assert property( + @(posedge clk_i) req_o |-> gnt_i |-> |gnt_o) + else $fatal (1, "Req out and grant in implies grant out."); + + gnt_idx : assert property( + @(posedge clk_i) req_o |-> gnt_i |-> gnt_o[idx_o]) + else $fatal (1, "Idx_o / gnt_o do not match."); + + req0 : assert property( + @(posedge clk_i) |req_i |-> req_o) + else $fatal (1, "Req in implies req out."); + + req1 : assert property( + @(posedge clk_i) req_o |-> |req_i) + else $fatal (1, "Req out implies req in."); + `endif + // pragma translate_on + end + +endmodule : rr_arb_tree
diff --git a/verilog/rtl/rstmgr.sv b/verilog/rtl/rstmgr.sv new file mode 100644 index 0000000..fca2e70 --- /dev/null +++ b/verilog/rtl/rstmgr.sv
@@ -0,0 +1,42 @@ + +// basic reset managemnet logic for azadi + +module rstmgr( + + input clk_i, //system clock + input rst_ni, // system reset + input prog_rst_ni, + + input logic ndmreset, // non-debug module reset + output logic sys_rst_ni // reset for system except debug module +); + + logic rst_d, rst_q; + logic rst_fd, rst_fq; // follower flip flop + + always_comb begin + if(!rst_ni) begin + rst_d = 1'b0; + end else + if(ndmreset) begin + rst_d = 1'b0; + end else + if(!prog_rst_ni)begin + rst_d = 1'b0; + end else begin + rst_d = 1'b1; + end + end + + always_ff @(posedge clk_i ) begin + rst_q <= rst_d; + end + + assign rst_fd = rst_q; + always_ff @(posedge clk_i ) begin + rst_fq <= rst_fd; + end + + assign sys_rst_ni = rst_fq; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/rv_dm.sv b/verilog/rtl/rv_dm.sv new file mode 100644 index 0000000..398652b --- /dev/null +++ b/verilog/rtl/rv_dm.sv
@@ -0,0 +1,319 @@ + + +module rv_dm #( + parameter int NrHarts = 1, + parameter logic [31:0] IdcodeValue = 32'h 0000_0001, + parameter logic DirectDmiTap = 1'b1 +) ( + input logic clk_i, // clock + input logic rst_ni, // asynchronous reset active low, connect PoR + // here, not the system reset + input logic testmode_i, + output logic ndmreset_o, // non-debug module reset + output logic dmactive_o, // debug module is active + output logic [NrHarts-1:0] debug_req_o, // async debug request + input logic [NrHarts-1:0] unavailable_i, // communicate whether the hart is unavailable + // (e.g.: power down) + + // bus device with debug memory, for an execution based technique + input tlul_pkg::tl_h2d_t tl_d_i, + output tlul_pkg::tl_d2h_t tl_d_o, + + // bus host, for system bus accesses + output tlul_pkg::tl_h2d_t tl_h_o, + input tlul_pkg::tl_d2h_t tl_h_i, + + input jtag_pkg::jtag_req_t jtag_req_i, + output jtag_pkg::jtag_rsp_t jtag_rsp_o +); + + + // Currently only 32 bit busses are supported by our TL-UL IP + localparam int BusWidth = 32; + // all harts have contiguous IDs + localparam logic [NrHarts-1:0] SelectableHarts = {NrHarts{1'b1}}; + + // Debug CSRs + dm::hartinfo_t [NrHarts-1:0] hartinfo; + logic [NrHarts-1:0] halted; + // logic [NrHarts-1:0] running; + logic [NrHarts-1:0] resumeack; + logic [NrHarts-1:0] haltreq; + logic [NrHarts-1:0] resumereq; + logic clear_resumeack; + logic cmd_valid; + dm::command_t cmd; + + logic cmderror_valid; + dm::cmderr_e cmderror; + logic cmdbusy; + logic [dm::ProgBufSize-1:0][31:0] progbuf; + logic [dm::DataCount-1:0][31:0] data_csrs_mem; + logic [dm::DataCount-1:0][31:0] data_mem_csrs; + logic data_valid; + logic [19:0] hartsel; + // System Bus Access Module + logic [BusWidth-1:0] sbaddress_csrs_sba; + logic [BusWidth-1:0] sbaddress_sba_csrs; + logic sbaddress_write_valid; + logic sbreadonaddr; + logic sbautoincrement; + logic [2:0] sbaccess; + logic sbreadondata; + logic [BusWidth-1:0] sbdata_write; + logic sbdata_read_valid; + logic sbdata_write_valid; + logic [BusWidth-1:0] sbdata_read; + logic sbdata_valid; + logic sbbusy; + logic sberror_valid; + logic [2:0] sberror; + + dm::dmi_req_t dmi_req; + dm::dmi_resp_t dmi_rsp; + logic dmi_req_valid, dmi_req_ready; + logic dmi_rsp_valid, dmi_rsp_ready; + logic dmi_rst_n; + + // static debug hartinfo + localparam dm::hartinfo_t DebugHartInfo = '{ + zero1: '0, + nscratch: 2, // Debug module needs at least two scratch regs + zero0: 0, + dataaccess: 1'b1, // data registers are memory mapped in the debugger + datasize: dm::DataCount, + dataaddr: dm::DataAddr + }; + for (genvar i = 0; i < NrHarts; i++) begin : gen_dm_hart_ctrl + assign hartinfo[i] = DebugHartInfo; + end + + dm_csrs #( + .NrHarts(NrHarts), + .BusWidth(BusWidth), + .SelectableHarts(SelectableHarts) + ) i_dm_csrs ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .testmode_i ( testmode_i ), + .dmi_rst_ni ( dmi_rst_n ), + .dmi_req_valid_i ( dmi_req_valid ), + .dmi_req_ready_o ( dmi_req_ready ), + .dmi_req_i ( dmi_req ), + .dmi_resp_valid_o ( dmi_rsp_valid ), + .dmi_resp_ready_i ( dmi_rsp_ready ), + .dmi_resp_o ( dmi_rsp ), + .ndmreset_o ( ndmreset_o ), + .dmactive_o ( dmactive_o ), + .hartsel_o ( hartsel ), + .hartinfo_i ( hartinfo ), + .halted_i ( halted ), + .unavailable_i, + .resumeack_i ( resumeack ), + .haltreq_o ( haltreq ), + .resumereq_o ( resumereq ), + .clear_resumeack_o ( clear_resumeack ), + .cmd_valid_o ( cmd_valid ), + .cmd_o ( cmd ), + .cmderror_valid_i ( cmderror_valid ), + .cmderror_i ( cmderror ), + .cmdbusy_i ( cmdbusy ), + .progbuf_o ( progbuf ), + .data_i ( data_mem_csrs ), + .data_valid_i ( data_valid ), + .data_o ( data_csrs_mem ), + .sbaddress_o ( sbaddress_csrs_sba ), + .sbaddress_i ( sbaddress_sba_csrs ), + .sbaddress_write_valid_o ( sbaddress_write_valid ), + .sbreadonaddr_o ( sbreadonaddr ), + .sbautoincrement_o ( sbautoincrement ), + .sbaccess_o ( sbaccess ), + .sbreadondata_o ( sbreadondata ), + .sbdata_o ( sbdata_write ), + .sbdata_read_valid_o ( sbdata_read_valid ), + .sbdata_write_valid_o ( sbdata_write_valid ), + .sbdata_i ( sbdata_read ), + .sbdata_valid_i ( sbdata_valid ), + .sbbusy_i ( sbbusy ), + .sberror_valid_i ( sberror_valid ), + .sberror_i ( sberror ) + ); + + logic host_req; + logic [BusWidth-1:0] host_add; + logic host_we; + logic [BusWidth-1:0] host_wdata; + logic [BusWidth/8-1:0] host_be; + logic host_gnt; + logic host_r_valid; + logic [BusWidth-1:0] host_r_rdata; + logic host_r_err; + + dm_sba #( + .BusWidth(BusWidth) + ) i_dm_sba ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .master_req_o ( host_req ), + .master_add_o ( host_add ), + .master_we_o ( host_we ), + .master_wdata_o ( host_wdata ), + .master_be_o ( host_be ), + .master_gnt_i ( host_gnt ), + .master_r_valid_i ( host_r_valid ), + .master_r_rdata_i ( host_r_rdata ), + .dmactive_i ( dmactive_o ), + .sbaddress_i ( sbaddress_csrs_sba ), + .sbaddress_o ( sbaddress_sba_csrs ), + .sbaddress_write_valid_i ( sbaddress_write_valid ), + .sbreadonaddr_i ( sbreadonaddr ), + .sbautoincrement_i ( sbautoincrement ), + .sbaccess_i ( sbaccess ), + .sbreadondata_i ( sbreadondata ), + .sbdata_i ( sbdata_write ), + .sbdata_read_valid_i ( sbdata_read_valid ), + .sbdata_write_valid_i ( sbdata_write_valid ), + .sbdata_o ( sbdata_read ), + .sbdata_valid_o ( sbdata_valid ), + .sbbusy_o ( sbbusy ), + .sberror_valid_o ( sberror_valid ), + .sberror_o ( sberror ) + ); + + tlul_host_adapter #( + .MAX_REQS(1) + ) tl_adapter_host_sba ( + .clk_i(clk_i), + .rst_ni(rst_ni), + .req_i (host_req), + .gnt_o (host_gnt), + .addr_i (host_add), + .we_i (host_we), + .wdata_i (host_wdata), + .be_i (host_be), + .valid_o (host_r_valid), + .rdata_o (host_r_rdata), + .err_o (host_r_err), + .tl_h_c_a (tl_h_o), + .tl_h_c_d (tl_h_i) + ); + + // DBG doesn't handle error responses so raise assertion if we see one + + localparam int unsigned AddressWidthWords = BusWidth - $clog2(BusWidth/8); + + logic req; + logic we; + logic [BusWidth/8-1:0] be; + logic [BusWidth-1:0] wdata; + logic [BusWidth-1:0] rdata; + logic rvalid; + + logic [BusWidth-1:0] addr_b; + logic [AddressWidthWords-1:0] addr_w; + + // TODO: The tlul_adapter_sram give us a bitwise write mask currently, + // but dm_mem only supports byte write masks. Disable sub-word access in the + // adapter for now until we figure out a good strategy to deal with this. + assign be = {BusWidth/8{1'b1}}; + + assign addr_b = {addr_w, {$clog2(BusWidth/8){1'b0}}}; + + dm_mem #( + .NrHarts(NrHarts), + .BusWidth(BusWidth), + .SelectableHarts(SelectableHarts), + // The debug module provides a simplified ROM for systems that map the debug ROM to offset 0x0 + // on the system bus. In that case, only one scratch register has to be implemented in the core. + // However, we require that the DM can be placed at arbitrary offsets in the system, which + // requires the generalized debug ROM implementation and two scratch registers. We hence set + // this parameter to a non-zero value (inside dm_mem, this just feeds into a comparison with 0). + .DmBaseAddress(1) + ) i_dm_mem ( + .clk_i ( clk_i ), + .rst_ni ( rst_ni ), + .debug_req_o ( debug_req_o ), + .hartsel_i ( hartsel ), + .haltreq_i ( haltreq ), + .resumereq_i ( resumereq ), + .clear_resumeack_i ( clear_resumeack ), + .halted_o ( halted ), + .resuming_o ( resumeack ), + .cmd_valid_i ( cmd_valid ), + .cmd_i ( cmd ), + .cmderror_valid_o ( cmderror_valid ), + .cmderror_o ( cmderror ), + .cmdbusy_o ( cmdbusy ), + .progbuf_i ( progbuf ), + .data_i ( data_csrs_mem ), + .data_o ( data_mem_csrs ), + .data_valid_o ( data_valid ), + .req_i ( req ), + .we_i ( we ), + .addr_i ( addr_b ), + .wdata_i ( wdata ), + .be_i ( be ), + .rdata_o ( rdata ) + ); + + // Bound-in DPI module replaces the TAP +//if (DirectDmiTap) begin + // JTAG TAP + dmi_jtag #( + .IdcodeValue (IdcodeValue) + ) dap ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .testmode_i (testmode_i), + + .dmi_rst_no (dmi_rst_n), + .dmi_req_o (dmi_req), + .dmi_req_valid_o (dmi_req_valid), + .dmi_req_ready_i (dmi_req_ready), + + .dmi_resp_i (dmi_rsp ), + .dmi_resp_ready_o (dmi_rsp_ready), + .dmi_resp_valid_i (dmi_rsp_valid), + + //JTAG + .tck_i (jtag_req_i.tck), + .tms_i (jtag_req_i.tms), + .trst_ni (jtag_req_i.trst_n), + .td_i (jtag_req_i.tdi), + .td_o (jtag_rsp_o.tdo), + .tdo_oe_o (jtag_rsp_o.tdo_oe) + ); +//end + + tlul_sram_adapter #( + .SramAw(AddressWidthWords), + .SramDw(BusWidth), + .Outstanding(1), + .ByteAccess(0) + ) tl_adapter_device_mem ( + .clk_i(clk_i), + .rst_ni(rst_ni), + + .req_o (req), + .gnt_i (1'b1), + .we_o (we), + .addr_o (addr_w), + .wdata_o (wdata), + .wmask_o (), + .rdata_i (rdata), + .rvalid_i (rvalid), + .rerror_i (2'b00), + + .tl_o (tl_d_o), + .tl_i (tl_d_i) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rvalid <= '0; + end else begin + rvalid <= req & ~we; + end + end + +endmodule
diff --git a/verilog/rtl/rv_plic.sv b/verilog/rtl/rv_plic.sv new file mode 100644 index 0000000..5d77a3b --- /dev/null +++ b/verilog/rtl/rv_plic.sv
@@ -0,0 +1,241 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// RISC-V Platform-Level Interrupt Controller compliant INTC +// +// Current version doesn't support MSI interrupt but it is easy to add +// the feature. Create one external register and connect qe signal to the +// gateway module (as edge-triggered) +// +// Consider to set MAX_PRIO as small number as possible. It is main factor +// of area increase if edge-triggered counter isn't implemented. +// +// Verilog parameter +// MAX_PRIO: Maximum value of interrupt priority + +module rv_plic import rv_plic_reg_pkg::*; #( + // derived parameter + localparam int SRCW = $clog2(NumSrc) +) ( + input clk_i, + input rst_ni, + + // Bus Interface (device) + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + // Interrupt Sources + input [NumSrc-1:0] intr_src_i, + + // Interrupt notification to targets + output [NumTarget-1:0] irq_o, + + output logic [NumTarget-1:0] msip_o +); + + rv_plic_reg2hw_t reg2hw; + rv_plic_hw2reg_t hw2reg; + + localparam int MAX_PRIO = 3; + localparam int PRIOW = $clog2(MAX_PRIO+1); + + logic [SRCW:0] irq_id_o [NumTarget]; + + logic [NumSrc-1:0] le; // 0:level 1:edge + logic [NumSrc-1:0] ip; + + logic [NumSrc-1:0] ie [NumTarget]; + + logic [NumTarget-1:0] claim_re; // Target read indicator + logic [SRCW-1:0] claim_id [NumTarget]; + logic [NumSrc-1:0] claim; // Converted from claim_re/claim_id + + logic [NumTarget-1:0] complete_we; // Target write indicator + logic [SRCW-1:0] complete_id [NumTarget]; + logic [NumSrc-1:0] complete; // Converted from complete_re/complete_id + + logic [SRCW:0] cc_id [NumTarget]; // Write ID + + logic [PRIOW-1:0] prio [NumSrc]; + + logic [PRIOW-1:0] threshold [NumTarget]; + + // Glue logic between rv_plic_reg_top and others + assign cc_id = irq_id_o; + + always_comb begin + for (int i = 0 ; i < NumTarget ; i++) begin + if (claim_re[i]) begin + claim[claim_id[i]] = 1'b1; + end else begin + claim = '0; + end + end + end + always_comb begin + for (int i = 0 ; i < NumTarget ; i++) begin + if (complete_we[i]) begin + complete[complete_id[i]] = 1'b1; + end else begin + complete = '0; + end + end + end + + //`ASSERT_PULSE(claimPulse, claim_re[i]) + //`ASSERT_PULSE(completePulse, complete_we[i]) + + + + ////////////// + // Priority // + ////////////// + assign prio[0] = reg2hw.prio0.q; + assign prio[1] = reg2hw.prio1.q; + assign prio[2] = reg2hw.prio2.q; + assign prio[3] = reg2hw.prio3.q; + assign prio[4] = reg2hw.prio4.q; + assign prio[5] = reg2hw.prio5.q; + assign prio[6] = reg2hw.prio6.q; + assign prio[7] = reg2hw.prio7.q; + assign prio[8] = reg2hw.prio8.q; + assign prio[9] = reg2hw.prio9.q; + assign prio[10] = reg2hw.prio10.q; + assign prio[11] = reg2hw.prio11.q; + assign prio[12] = reg2hw.prio12.q; + assign prio[13] = reg2hw.prio13.q; + assign prio[14] = reg2hw.prio14.q; + assign prio[15] = reg2hw.prio15.q; + assign prio[16] = reg2hw.prio16.q; + assign prio[17] = reg2hw.prio17.q; + assign prio[18] = reg2hw.prio18.q; + assign prio[19] = reg2hw.prio19.q; + assign prio[20] = reg2hw.prio20.q; + assign prio[21] = reg2hw.prio21.q; + assign prio[22] = reg2hw.prio22.q; + assign prio[23] = reg2hw.prio23.q; + assign prio[24] = reg2hw.prio24.q; + assign prio[25] = reg2hw.prio25.q; + assign prio[26] = reg2hw.prio26.q; + assign prio[27] = reg2hw.prio27.q; + assign prio[28] = reg2hw.prio28.q; + assign prio[29] = reg2hw.prio29.q; + assign prio[30] = reg2hw.prio30.q; + assign prio[31] = reg2hw.prio31.q; + assign prio[32] = reg2hw.prio32.q; + assign prio[33] = reg2hw.prio33.q; + assign prio[34] = reg2hw.prio34.q; + assign prio[35] = reg2hw.prio35.q; + assign prio[36] = reg2hw.prio36.q; + assign prio[37] = reg2hw.prio37.q; + assign prio[38] = reg2hw.prio38.q; + assign prio[39] = reg2hw.prio39.q; + assign prio[40] = reg2hw.prio40.q; + assign prio[41] = reg2hw.prio41.q; + assign prio[42] = reg2hw.prio42.q; + assign prio[43] = reg2hw.prio43.q; + ////////////////////// + // Interrupt Enable // + ////////////////////// + for (genvar s = 0; s < 44; s++) begin : gen_ie0 + assign ie[0][s] = reg2hw.ie0[s].q; + end + + //////////////////////// + // THRESHOLD register // + //////////////////////// + assign threshold[0] = reg2hw.threshold0.q; + + ///////////////// + // CC register // + ///////////////// + assign claim_re[0] = reg2hw.cc0.re; + assign claim_id[0] = irq_id_o[0]; + assign complete_we[0] = reg2hw.cc0.qe; + assign complete_id[0] = reg2hw.cc0.q; + assign hw2reg.cc0.d = cc_id[0]; + + /////////////////// + // MSIP register // + /////////////////// + assign msip_o[0] = reg2hw.msip0.q; + + //////// + // IP // + //////// + for (genvar s = 0; s < 44; s++) begin : gen_ip + assign hw2reg.ip[s].de = 1'b1; // Always write + assign hw2reg.ip[s].d = ip[s]; + end + + /////////////////////////////////// + // Detection:: 0: Level, 1: Edge // + /////////////////////////////////// + for (genvar s = 0; s < 44; s++) begin : gen_le + assign le[s] = reg2hw.le[s].q; + end + + ////////////// + // Gateways // + ////////////// + rv_plic_gateway #( + .N_SOURCE (NumSrc) + ) u_gateway ( + .clk_i, + .rst_ni, + + .src_i (intr_src_i), + .le_i (le), + + .claim_i (claim), + .complete_i (complete), + + .ip_o (ip) + ); + + /////////////////////////////////// + // Target interrupt notification // + /////////////////////////////////// + for (genvar i = 0 ; i < NumTarget ; i++) begin : gen_target + rv_plic_target #( + .N_SOURCE (NumSrc), + .MAX_PRIO (MAX_PRIO) + ) u_target ( + .clk_i, + .rst_ni, + + .ip_i (ip), + .ie_i (ie[i]), + + .prio_i (prio), + .threshold_i (threshold[i]), + + .irq_o (irq_o[i]), + .irq_id_o (irq_id_o[i]) + + ); + end + + //////////////////////// + // Register interface // + //////////////////////// + // Limitation of register tool prevents the module from having flexibility to parameters + // So, signals are manually tied at the top. + rv_plic_reg_top u_reg ( + .clk_i, + .rst_ni, + + .tl_i, + .tl_o, + + .reg2hw, + .hw2reg, + + .devmode_i (1'b1) + ); + + + +endmodule +
diff --git a/verilog/rtl/rv_plic_gateway.sv b/verilog/rtl/rv_plic_gateway.sv new file mode 100644 index 0000000..c81810b --- /dev/null +++ b/verilog/rtl/rv_plic_gateway.sv
@@ -0,0 +1,62 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// RISC-V Platform-Level Interrupt Gateways module + +module rv_plic_gateway #( + parameter int N_SOURCE = 32 +) ( + input clk_i, + input rst_ni, + + input [N_SOURCE-1:0] src_i, + input [N_SOURCE-1:0] le_i, // Level0 Edge1 + + input [N_SOURCE-1:0] claim_i, // $onehot0(claim_i) + input [N_SOURCE-1:0] complete_i, // $onehot0(complete_i) + + output logic [N_SOURCE-1:0] ip_o +); + + logic [N_SOURCE-1:0] ia; // Interrupt Active + + logic [N_SOURCE-1:0] set; // Set: (le_i) ? src_i & ~src_q : src_i ; + logic [N_SOURCE-1:0] src_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) src_q <= '0; + else src_q <= src_i; + end + + always_comb begin + for (int i = 0 ; i < N_SOURCE; i++) begin + set[i] = (le_i[i]) ? src_i[i] & ~src_q[i] : src_i[i] ; + end + end + + // Interrupt pending is set by source (depends on le_i), cleared by claim_i. + // Until interrupt is claimed, set doesn't affect ip_o. + // RISC-V PLIC spec mentioned it can have counter for edge triggered + // But skipped the feature as counter consumes substantial logic size. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ip_o <= '0; + end else begin + ip_o <= (ip_o | (set & ~ia & ~ip_o)) & (~(ip_o & claim_i)); + end + end + + // Interrupt active is to control ip_o. If ip_o is set then until completed + // by target, ip_o shouldn't be set by source even claim_i can clear ip_o. + // ia can be cleared only when ia was set. If `set` and `complete_i` happen + // at the same time, always `set` wins. + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + ia <= '0; + end else begin + ia <= (ia | (set & ~ia)) & (~(ia & complete_i & ~ip_o)); + end + end + +endmodule
diff --git a/verilog/rtl/rv_plic_reg_pkg.sv b/verilog/rtl/rv_plic_reg_pkg.sv new file mode 100644 index 0000000..ca7182f --- /dev/null +++ b/verilog/rtl/rv_plic_reg_pkg.sv
@@ -0,0 +1,462 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package rv_plic_reg_pkg; + + // Param list + parameter int NumSrc = 44; + parameter int NumTarget = 1; + parameter int PrioWidth = 2; + + // Address width within the block + parameter int BlockAw = 10; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + typedef struct packed { + logic q; + } rv_plic_reg2hw_le_mreg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio0_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio1_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio2_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio3_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio4_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio5_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio6_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio7_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio8_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio9_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio10_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio11_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio12_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio13_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio14_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio15_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio16_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio17_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio18_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio19_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio20_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio21_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio22_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio23_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio24_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio25_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio26_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio27_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio28_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio29_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio30_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio31_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio32_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio33_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio34_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio35_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio36_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio37_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio38_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio39_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio40_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio41_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio42_reg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_prio43_reg_t; + + typedef struct packed { + logic q; + } rv_plic_reg2hw_ie0_mreg_t; + + typedef struct packed { + logic [1:0] q; + } rv_plic_reg2hw_threshold0_reg_t; + + typedef struct packed { + logic [7:0] q; + logic qe; + logic re; + } rv_plic_reg2hw_cc0_reg_t; + + typedef struct packed { + logic q; + } rv_plic_reg2hw_msip0_reg_t; + + + typedef struct packed { + logic d; + logic de; + } rv_plic_hw2reg_ip_mreg_t; + + typedef struct packed { + logic [7:0] d; + } rv_plic_hw2reg_cc0_reg_t; + + + /////////////////////////////////////// + // Register to internal design logic // + /////////////////////////////////////// + typedef struct packed { + rv_plic_reg2hw_le_mreg_t [43:0] le; // [700:529] + rv_plic_reg2hw_prio0_reg_t prio0; // [528:527] + rv_plic_reg2hw_prio1_reg_t prio1; // [526:525] + rv_plic_reg2hw_prio2_reg_t prio2; // [524:523] + rv_plic_reg2hw_prio3_reg_t prio3; // [522:521] + rv_plic_reg2hw_prio4_reg_t prio4; // [520:519] + rv_plic_reg2hw_prio5_reg_t prio5; // [518:517] + rv_plic_reg2hw_prio6_reg_t prio6; // [516:515] + rv_plic_reg2hw_prio7_reg_t prio7; // [514:513] + rv_plic_reg2hw_prio8_reg_t prio8; // [512:511] + rv_plic_reg2hw_prio9_reg_t prio9; // [510:509] + rv_plic_reg2hw_prio10_reg_t prio10; // [508:507] + rv_plic_reg2hw_prio11_reg_t prio11; // [506:505] + rv_plic_reg2hw_prio12_reg_t prio12; // [504:503] + rv_plic_reg2hw_prio13_reg_t prio13; // [502:501] + rv_plic_reg2hw_prio14_reg_t prio14; // [500:499] + rv_plic_reg2hw_prio15_reg_t prio15; // [498:497] + rv_plic_reg2hw_prio16_reg_t prio16; // [496:495] + rv_plic_reg2hw_prio17_reg_t prio17; // [494:493] + rv_plic_reg2hw_prio18_reg_t prio18; // [492:491] + rv_plic_reg2hw_prio19_reg_t prio19; // [490:489] + rv_plic_reg2hw_prio20_reg_t prio20; // [488:487] + rv_plic_reg2hw_prio21_reg_t prio21; // [486:485] + rv_plic_reg2hw_prio22_reg_t prio22; // [484:483] + rv_plic_reg2hw_prio23_reg_t prio23; // [482:481] + rv_plic_reg2hw_prio24_reg_t prio24; // [480:479] + rv_plic_reg2hw_prio25_reg_t prio25; // [478:477] + rv_plic_reg2hw_prio26_reg_t prio26; // [476:475] + rv_plic_reg2hw_prio27_reg_t prio27; // [474:473] + rv_plic_reg2hw_prio28_reg_t prio28; // [472:471] + rv_plic_reg2hw_prio29_reg_t prio29; // [470:469] + rv_plic_reg2hw_prio30_reg_t prio30; // [468:467] + rv_plic_reg2hw_prio31_reg_t prio31; // [466:465] + rv_plic_reg2hw_prio32_reg_t prio32; // [464:463] + rv_plic_reg2hw_prio33_reg_t prio33; // [462:461] + rv_plic_reg2hw_prio34_reg_t prio34; // [460:459] + rv_plic_reg2hw_prio35_reg_t prio35; // [458:457] + rv_plic_reg2hw_prio36_reg_t prio36; // [456:455] + rv_plic_reg2hw_prio37_reg_t prio37; // [454:453] + rv_plic_reg2hw_prio38_reg_t prio38; // [452:451] + rv_plic_reg2hw_prio39_reg_t prio39; // [450:449] + rv_plic_reg2hw_prio40_reg_t prio40; // [448:447] + rv_plic_reg2hw_prio41_reg_t prio41; // [446:445] + rv_plic_reg2hw_prio42_reg_t prio42; // [444:443] + rv_plic_reg2hw_prio43_reg_t prio43; // [442:441] + rv_plic_reg2hw_ie0_mreg_t [43:0] ie0; // [184:13] + rv_plic_reg2hw_threshold0_reg_t threshold0; // [12:11] + rv_plic_reg2hw_cc0_reg_t cc0; // [10:1] + rv_plic_reg2hw_msip0_reg_t msip0; // [0:0] + } rv_plic_reg2hw_t; + + /////////////////////////////////////// + // Internal design logic to register // + /////////////////////////////////////// + typedef struct packed { + rv_plic_hw2reg_ip_mreg_t [43:0] ip; // [351:8] + rv_plic_hw2reg_cc0_reg_t cc0; // [7:0] + } rv_plic_hw2reg_t; + + // Register Address + parameter logic [BlockAw-1:0] RV_PLIC_IP_0_OFFSET = 10'h 0; + parameter logic [BlockAw-1:0] RV_PLIC_IP_1_OFFSET = 10'h 4; + parameter logic [BlockAw-1:0] RV_PLIC_LE_0_OFFSET = 10'h 8; + parameter logic [BlockAw-1:0] RV_PLIC_LE_1_OFFSET = 10'h c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO0_OFFSET = 10'h 10; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO1_OFFSET = 10'h 14; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO2_OFFSET = 10'h 18; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO3_OFFSET = 10'h 1c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO4_OFFSET = 10'h 20; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO5_OFFSET = 10'h 24; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO6_OFFSET = 10'h 28; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO7_OFFSET = 10'h 2c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO8_OFFSET = 10'h 30; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO9_OFFSET = 10'h 34; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO10_OFFSET = 10'h 38; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO11_OFFSET = 10'h 3c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO12_OFFSET = 10'h 40; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO13_OFFSET = 10'h 44; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO14_OFFSET = 10'h 48; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO15_OFFSET = 10'h 4c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO16_OFFSET = 10'h 50; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO17_OFFSET = 10'h 54; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO18_OFFSET = 10'h 58; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO19_OFFSET = 10'h 5c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO20_OFFSET = 10'h 60; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO21_OFFSET = 10'h 64; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO22_OFFSET = 10'h 68; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO23_OFFSET = 10'h 6c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO24_OFFSET = 10'h 70; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO25_OFFSET = 10'h 74; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO26_OFFSET = 10'h 78; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO27_OFFSET = 10'h 7c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO28_OFFSET = 10'h 80; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO29_OFFSET = 10'h 84; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO30_OFFSET = 10'h 88; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO31_OFFSET = 10'h 8c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO32_OFFSET = 10'h 90; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO33_OFFSET = 10'h 94; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO34_OFFSET = 10'h 98; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO35_OFFSET = 10'h 9c; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO36_OFFSET = 10'h a0; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO37_OFFSET = 10'h a4; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO38_OFFSET = 10'h a8; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO39_OFFSET = 10'h ac; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO40_OFFSET = 10'h b0; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO41_OFFSET = 10'h b4; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO42_OFFSET = 10'h b8; + parameter logic [BlockAw-1:0] RV_PLIC_PRIO43_OFFSET = 10'h bc; + parameter logic [BlockAw-1:0] RV_PLIC_IE0_0_OFFSET = 10'h c0; + parameter logic [BlockAw-1:0] RV_PLIC_IE0_1_OFFSET = 10'h c4; + parameter logic [BlockAw-1:0] RV_PLIC_THRESHOLD0_OFFSET = 10'h c8; + parameter logic [BlockAw-1:0] RV_PLIC_CC0_OFFSET = 10'h cc; + parameter logic [BlockAw-1:0] RV_PLIC_MSIP0_OFFSET = 10'h d0; + + + // Register Index + typedef enum int { + RV_PLIC_IP_0, + RV_PLIC_IP_1, + RV_PLIC_LE_0, + RV_PLIC_LE_1, + RV_PLIC_PRIO0, + RV_PLIC_PRIO1, + RV_PLIC_PRIO2, + RV_PLIC_PRIO3, + RV_PLIC_PRIO4, + RV_PLIC_PRIO5, + RV_PLIC_PRIO6, + RV_PLIC_PRIO7, + RV_PLIC_PRIO8, + RV_PLIC_PRIO9, + RV_PLIC_PRIO10, + RV_PLIC_PRIO11, + RV_PLIC_PRIO12, + RV_PLIC_PRIO13, + RV_PLIC_PRIO14, + RV_PLIC_PRIO15, + RV_PLIC_PRIO16, + RV_PLIC_PRIO17, + RV_PLIC_PRIO18, + RV_PLIC_PRIO19, + RV_PLIC_PRIO20, + RV_PLIC_PRIO21, + RV_PLIC_PRIO22, + RV_PLIC_PRIO23, + RV_PLIC_PRIO24, + RV_PLIC_PRIO25, + RV_PLIC_PRIO26, + RV_PLIC_PRIO27, + RV_PLIC_PRIO28, + RV_PLIC_PRIO29, + RV_PLIC_PRIO30, + RV_PLIC_PRIO31, + RV_PLIC_PRIO32, + RV_PLIC_PRIO33, + RV_PLIC_PRIO34, + RV_PLIC_PRIO35, + RV_PLIC_PRIO36, + RV_PLIC_PRIO37, + RV_PLIC_PRIO38, + RV_PLIC_PRIO39, + RV_PLIC_PRIO40, + RV_PLIC_PRIO41, + RV_PLIC_PRIO42, + RV_PLIC_PRIO43, + RV_PLIC_IE0_0, + RV_PLIC_IE0_1, + RV_PLIC_THRESHOLD0, + RV_PLIC_CC0, + RV_PLIC_MSIP0 + } rv_plic_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] RV_PLIC_PERMIT [53] = '{ + 4'b 1111, // index[ 0] RV_PLIC_IP_0 + 4'b 1111, // index[ 1] RV_PLIC_IP_1 + 4'b 1111, // index[ 6] RV_PLIC_LE_0 + 4'b 1111, // index[ 7] RV_PLIC_LE_1 + 4'b 0001, // index[ 12] RV_PLIC_PRIO0 + 4'b 0001, // index[ 13] RV_PLIC_PRIO1 + 4'b 0001, // index[ 14] RV_PLIC_PRIO2 + 4'b 0001, // index[ 15] RV_PLIC_PRIO3 + 4'b 0001, // index[ 16] RV_PLIC_PRIO4 + 4'b 0001, // index[ 17] RV_PLIC_PRIO5 + 4'b 0001, // index[ 18] RV_PLIC_PRIO6 + 4'b 0001, // index[ 19] RV_PLIC_PRIO7 + 4'b 0001, // index[ 20] RV_PLIC_PRIO8 + 4'b 0001, // index[ 21] RV_PLIC_PRIO9 + 4'b 0001, // index[ 22] RV_PLIC_PRIO10 + 4'b 0001, // index[ 23] RV_PLIC_PRIO11 + 4'b 0001, // index[ 24] RV_PLIC_PRIO12 + 4'b 0001, // index[ 25] RV_PLIC_PRIO13 + 4'b 0001, // index[ 26] RV_PLIC_PRIO14 + 4'b 0001, // index[ 27] RV_PLIC_PRIO15 + 4'b 0001, // index[ 28] RV_PLIC_PRIO16 + 4'b 0001, // index[ 29] RV_PLIC_PRIO17 + 4'b 0001, // index[ 30] RV_PLIC_PRIO18 + 4'b 0001, // index[ 31] RV_PLIC_PRIO19 + 4'b 0001, // index[ 32] RV_PLIC_PRIO20 + 4'b 0001, // index[ 33] RV_PLIC_PRIO21 + 4'b 0001, // index[ 34] RV_PLIC_PRIO22 + 4'b 0001, // index[ 35] RV_PLIC_PRIO23 + 4'b 0001, // index[ 36] RV_PLIC_PRIO24 + 4'b 0001, // index[ 37] RV_PLIC_PRIO25 + 4'b 0001, // index[ 38] RV_PLIC_PRIO26 + 4'b 0001, // index[ 39] RV_PLIC_PRIO27 + 4'b 0001, // index[ 40] RV_PLIC_PRIO28 + 4'b 0001, // index[ 41] RV_PLIC_PRIO29 + 4'b 0001, // index[ 42] RV_PLIC_PRIO30 + 4'b 0001, // index[ 43] RV_PLIC_PRIO31 + 4'b 0001, // index[ 44] RV_PLIC_PRIO32 + 4'b 0001, // index[ 45] RV_PLIC_PRIO33 + 4'b 0001, // index[ 46] RV_PLIC_PRIO34 + 4'b 0001, // index[ 47] RV_PLIC_PRIO35 + 4'b 0001, // index[ 48] RV_PLIC_PRIO36 + 4'b 0001, // index[ 49] RV_PLIC_PRIO37 + 4'b 0001, // index[ 50] RV_PLIC_PRIO38 + 4'b 0001, // index[ 51] RV_PLIC_PRIO39 + 4'b 0001, // index[ 52] RV_PLIC_PRIO40 + 4'b 0001, // index[ 53] RV_PLIC_PRIO41 + 4'b 0001, // index[ 54] RV_PLIC_PRIO42 + 4'b 0001, // index[ 55] RV_PLIC_PRIO43 + 4'b 1111, // index[184] RV_PLIC_IE0_0 + 4'b 1111, // index[185] RV_PLIC_IE0_1 + 4'b 0001, // index[190] RV_PLIC_THRESHOLD0 + 4'b 0001, // index[191] RV_PLIC_CC0 + 4'b 0001 // index[192] RV_PLIC_MSIP0 + }; +endpackage +
diff --git a/verilog/rtl/rv_plic_reg_top.sv b/verilog/rtl/rv_plic_reg_top.sv new file mode 100644 index 0000000..d7c1e35 --- /dev/null +++ b/verilog/rtl/rv_plic_reg_top.sv
@@ -0,0 +1,6065 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +module rv_plic_reg_top ( + input clk_i, + input rst_ni, + + // Below Regster interface can be changed + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // To HW + output rv_plic_reg_pkg::rv_plic_reg2hw_t reg2hw, // Write + input rv_plic_reg_pkg::rv_plic_hw2reg_t hw2reg, // Read + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import rv_plic_reg_pkg::* ; + + localparam int AW = 10; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + tlul_pkg::tl_h2d_t tl_reg_h2d; + tlul_pkg::tl_d2h_t tl_reg_d2h; + + assign tl_reg_h2d = tl_i; + assign tl_o = tl_reg_d2h; + + tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW) + ) u_reg_if ( + .clk_i, + .rst_ni, + + .tl_i (tl_reg_h2d), + .tl_o (tl_reg_d2h), + + .we_o (reg_we), + .re_o (reg_re), + .addr_o (reg_addr), + .wdata_o (reg_wdata), + .be_o (reg_be), + .rdata_i (reg_rdata), + .error_i (reg_error) + ); + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err ; + + // Define SW related signals + // Format: <reg>_<field>_{wd|we|qs} + // or <reg>_{wd|we|qs} if field == 1 or 0 + logic ip_0_p_0_qs; + logic ip_0_p_1_qs; + logic ip_0_p_2_qs; + logic ip_0_p_3_qs; + logic ip_0_p_4_qs; + logic ip_0_p_5_qs; + logic ip_0_p_6_qs; + logic ip_0_p_7_qs; + logic ip_0_p_8_qs; + logic ip_0_p_9_qs; + logic ip_0_p_10_qs; + logic ip_0_p_11_qs; + logic ip_0_p_12_qs; + logic ip_0_p_13_qs; + logic ip_0_p_14_qs; + logic ip_0_p_15_qs; + logic ip_0_p_16_qs; + logic ip_0_p_17_qs; + logic ip_0_p_18_qs; + logic ip_0_p_19_qs; + logic ip_0_p_20_qs; + logic ip_0_p_21_qs; + logic ip_0_p_22_qs; + logic ip_0_p_23_qs; + logic ip_0_p_24_qs; + logic ip_0_p_25_qs; + logic ip_0_p_26_qs; + logic ip_0_p_27_qs; + logic ip_0_p_28_qs; + logic ip_0_p_29_qs; + logic ip_0_p_30_qs; + logic ip_0_p_31_qs; + logic ip_1_p_32_qs; + logic ip_1_p_33_qs; + logic ip_1_p_34_qs; + logic ip_1_p_35_qs; + logic ip_1_p_36_qs; + logic ip_1_p_37_qs; + logic ip_1_p_38_qs; + logic ip_1_p_39_qs; + logic ip_1_p_40_qs; + logic ip_1_p_41_qs; + logic ip_1_p_42_qs; + logic ip_1_p_43_qs; + logic le_0_le_0_qs; + logic le_0_le_0_wd; + logic le_0_le_0_we; + logic le_0_le_1_qs; + logic le_0_le_1_wd; + logic le_0_le_1_we; + logic le_0_le_2_qs; + logic le_0_le_2_wd; + logic le_0_le_2_we; + logic le_0_le_3_qs; + logic le_0_le_3_wd; + logic le_0_le_3_we; + logic le_0_le_4_qs; + logic le_0_le_4_wd; + logic le_0_le_4_we; + logic le_0_le_5_qs; + logic le_0_le_5_wd; + logic le_0_le_5_we; + logic le_0_le_6_qs; + logic le_0_le_6_wd; + logic le_0_le_6_we; + logic le_0_le_7_qs; + logic le_0_le_7_wd; + logic le_0_le_7_we; + logic le_0_le_8_qs; + logic le_0_le_8_wd; + logic le_0_le_8_we; + logic le_0_le_9_qs; + logic le_0_le_9_wd; + logic le_0_le_9_we; + logic le_0_le_10_qs; + logic le_0_le_10_wd; + logic le_0_le_10_we; + logic le_0_le_11_qs; + logic le_0_le_11_wd; + logic le_0_le_11_we; + logic le_0_le_12_qs; + logic le_0_le_12_wd; + logic le_0_le_12_we; + logic le_0_le_13_qs; + logic le_0_le_13_wd; + logic le_0_le_13_we; + logic le_0_le_14_qs; + logic le_0_le_14_wd; + logic le_0_le_14_we; + logic le_0_le_15_qs; + logic le_0_le_15_wd; + logic le_0_le_15_we; + logic le_0_le_16_qs; + logic le_0_le_16_wd; + logic le_0_le_16_we; + logic le_0_le_17_qs; + logic le_0_le_17_wd; + logic le_0_le_17_we; + logic le_0_le_18_qs; + logic le_0_le_18_wd; + logic le_0_le_18_we; + logic le_0_le_19_qs; + logic le_0_le_19_wd; + logic le_0_le_19_we; + logic le_0_le_20_qs; + logic le_0_le_20_wd; + logic le_0_le_20_we; + logic le_0_le_21_qs; + logic le_0_le_21_wd; + logic le_0_le_21_we; + logic le_0_le_22_qs; + logic le_0_le_22_wd; + logic le_0_le_22_we; + logic le_0_le_23_qs; + logic le_0_le_23_wd; + logic le_0_le_23_we; + logic le_0_le_24_qs; + logic le_0_le_24_wd; + logic le_0_le_24_we; + logic le_0_le_25_qs; + logic le_0_le_25_wd; + logic le_0_le_25_we; + logic le_0_le_26_qs; + logic le_0_le_26_wd; + logic le_0_le_26_we; + logic le_0_le_27_qs; + logic le_0_le_27_wd; + logic le_0_le_27_we; + logic le_0_le_28_qs; + logic le_0_le_28_wd; + logic le_0_le_28_we; + logic le_0_le_29_qs; + logic le_0_le_29_wd; + logic le_0_le_29_we; + logic le_0_le_30_qs; + logic le_0_le_30_wd; + logic le_0_le_30_we; + logic le_0_le_31_qs; + logic le_0_le_31_wd; + logic le_0_le_31_we; + logic le_1_le_32_qs; + logic le_1_le_32_wd; + logic le_1_le_32_we; + logic le_1_le_33_qs; + logic le_1_le_33_wd; + logic le_1_le_33_we; + logic le_1_le_34_qs; + logic le_1_le_34_wd; + logic le_1_le_34_we; + logic le_1_le_35_qs; + logic le_1_le_35_wd; + logic le_1_le_35_we; + logic le_1_le_36_qs; + logic le_1_le_36_wd; + logic le_1_le_36_we; + logic le_1_le_37_qs; + logic le_1_le_37_wd; + logic le_1_le_37_we; + logic le_1_le_38_qs; + logic le_1_le_38_wd; + logic le_1_le_38_we; + logic le_1_le_39_qs; + logic le_1_le_39_wd; + logic le_1_le_39_we; + logic le_1_le_40_qs; + logic le_1_le_40_wd; + logic le_1_le_40_we; + logic le_1_le_41_qs; + logic le_1_le_41_wd; + logic le_1_le_41_we; + logic le_1_le_42_qs; + logic le_1_le_42_wd; + logic le_1_le_42_we; + logic le_1_le_43_qs; + logic le_1_le_43_wd; + logic le_1_le_43_we; + logic [1:0] prio0_qs; + logic [1:0] prio0_wd; + logic prio0_we; + logic [1:0] prio1_qs; + logic [1:0] prio1_wd; + logic prio1_we; + logic [1:0] prio2_qs; + logic [1:0] prio2_wd; + logic prio2_we; + logic [1:0] prio3_qs; + logic [1:0] prio3_wd; + logic prio3_we; + logic [1:0] prio4_qs; + logic [1:0] prio4_wd; + logic prio4_we; + logic [1:0] prio5_qs; + logic [1:0] prio5_wd; + logic prio5_we; + logic [1:0] prio6_qs; + logic [1:0] prio6_wd; + logic prio6_we; + logic [1:0] prio7_qs; + logic [1:0] prio7_wd; + logic prio7_we; + logic [1:0] prio8_qs; + logic [1:0] prio8_wd; + logic prio8_we; + logic [1:0] prio9_qs; + logic [1:0] prio9_wd; + logic prio9_we; + logic [1:0] prio10_qs; + logic [1:0] prio10_wd; + logic prio10_we; + logic [1:0] prio11_qs; + logic [1:0] prio11_wd; + logic prio11_we; + logic [1:0] prio12_qs; + logic [1:0] prio12_wd; + logic prio12_we; + logic [1:0] prio13_qs; + logic [1:0] prio13_wd; + logic prio13_we; + logic [1:0] prio14_qs; + logic [1:0] prio14_wd; + logic prio14_we; + logic [1:0] prio15_qs; + logic [1:0] prio15_wd; + logic prio15_we; + logic [1:0] prio16_qs; + logic [1:0] prio16_wd; + logic prio16_we; + logic [1:0] prio17_qs; + logic [1:0] prio17_wd; + logic prio17_we; + logic [1:0] prio18_qs; + logic [1:0] prio18_wd; + logic prio18_we; + logic [1:0] prio19_qs; + logic [1:0] prio19_wd; + logic prio19_we; + logic [1:0] prio20_qs; + logic [1:0] prio20_wd; + logic prio20_we; + logic [1:0] prio21_qs; + logic [1:0] prio21_wd; + logic prio21_we; + logic [1:0] prio22_qs; + logic [1:0] prio22_wd; + logic prio22_we; + logic [1:0] prio23_qs; + logic [1:0] prio23_wd; + logic prio23_we; + logic [1:0] prio24_qs; + logic [1:0] prio24_wd; + logic prio24_we; + logic [1:0] prio25_qs; + logic [1:0] prio25_wd; + logic prio25_we; + logic [1:0] prio26_qs; + logic [1:0] prio26_wd; + logic prio26_we; + logic [1:0] prio27_qs; + logic [1:0] prio27_wd; + logic prio27_we; + logic [1:0] prio28_qs; + logic [1:0] prio28_wd; + logic prio28_we; + logic [1:0] prio29_qs; + logic [1:0] prio29_wd; + logic prio29_we; + logic [1:0] prio30_qs; + logic [1:0] prio30_wd; + logic prio30_we; + logic [1:0] prio31_qs; + logic [1:0] prio31_wd; + logic prio31_we; + logic [1:0] prio32_qs; + logic [1:0] prio32_wd; + logic prio32_we; + logic [1:0] prio33_qs; + logic [1:0] prio33_wd; + logic prio33_we; + logic [1:0] prio34_qs; + logic [1:0] prio34_wd; + logic prio34_we; + logic [1:0] prio35_qs; + logic [1:0] prio35_wd; + logic prio35_we; + logic [1:0] prio36_qs; + logic [1:0] prio36_wd; + logic prio36_we; + logic [1:0] prio37_qs; + logic [1:0] prio37_wd; + logic prio37_we; + logic [1:0] prio38_qs; + logic [1:0] prio38_wd; + logic prio38_we; + logic [1:0] prio39_qs; + logic [1:0] prio39_wd; + logic prio39_we; + logic [1:0] prio40_qs; + logic [1:0] prio40_wd; + logic prio40_we; + logic [1:0] prio41_qs; + logic [1:0] prio41_wd; + logic prio41_we; + logic [1:0] prio42_qs; + logic [1:0] prio42_wd; + logic prio42_we; + logic [1:0] prio43_qs; + logic [1:0] prio43_wd; + logic prio43_we; + logic ie0_0_e_0_qs; + logic ie0_0_e_0_wd; + logic ie0_0_e_0_we; + logic ie0_0_e_1_qs; + logic ie0_0_e_1_wd; + logic ie0_0_e_1_we; + logic ie0_0_e_2_qs; + logic ie0_0_e_2_wd; + logic ie0_0_e_2_we; + logic ie0_0_e_3_qs; + logic ie0_0_e_3_wd; + logic ie0_0_e_3_we; + logic ie0_0_e_4_qs; + logic ie0_0_e_4_wd; + logic ie0_0_e_4_we; + logic ie0_0_e_5_qs; + logic ie0_0_e_5_wd; + logic ie0_0_e_5_we; + logic ie0_0_e_6_qs; + logic ie0_0_e_6_wd; + logic ie0_0_e_6_we; + logic ie0_0_e_7_qs; + logic ie0_0_e_7_wd; + logic ie0_0_e_7_we; + logic ie0_0_e_8_qs; + logic ie0_0_e_8_wd; + logic ie0_0_e_8_we; + logic ie0_0_e_9_qs; + logic ie0_0_e_9_wd; + logic ie0_0_e_9_we; + logic ie0_0_e_10_qs; + logic ie0_0_e_10_wd; + logic ie0_0_e_10_we; + logic ie0_0_e_11_qs; + logic ie0_0_e_11_wd; + logic ie0_0_e_11_we; + logic ie0_0_e_12_qs; + logic ie0_0_e_12_wd; + logic ie0_0_e_12_we; + logic ie0_0_e_13_qs; + logic ie0_0_e_13_wd; + logic ie0_0_e_13_we; + logic ie0_0_e_14_qs; + logic ie0_0_e_14_wd; + logic ie0_0_e_14_we; + logic ie0_0_e_15_qs; + logic ie0_0_e_15_wd; + logic ie0_0_e_15_we; + logic ie0_0_e_16_qs; + logic ie0_0_e_16_wd; + logic ie0_0_e_16_we; + logic ie0_0_e_17_qs; + logic ie0_0_e_17_wd; + logic ie0_0_e_17_we; + logic ie0_0_e_18_qs; + logic ie0_0_e_18_wd; + logic ie0_0_e_18_we; + logic ie0_0_e_19_qs; + logic ie0_0_e_19_wd; + logic ie0_0_e_19_we; + logic ie0_0_e_20_qs; + logic ie0_0_e_20_wd; + logic ie0_0_e_20_we; + logic ie0_0_e_21_qs; + logic ie0_0_e_21_wd; + logic ie0_0_e_21_we; + logic ie0_0_e_22_qs; + logic ie0_0_e_22_wd; + logic ie0_0_e_22_we; + logic ie0_0_e_23_qs; + logic ie0_0_e_23_wd; + logic ie0_0_e_23_we; + logic ie0_0_e_24_qs; + logic ie0_0_e_24_wd; + logic ie0_0_e_24_we; + logic ie0_0_e_25_qs; + logic ie0_0_e_25_wd; + logic ie0_0_e_25_we; + logic ie0_0_e_26_qs; + logic ie0_0_e_26_wd; + logic ie0_0_e_26_we; + logic ie0_0_e_27_qs; + logic ie0_0_e_27_wd; + logic ie0_0_e_27_we; + logic ie0_0_e_28_qs; + logic ie0_0_e_28_wd; + logic ie0_0_e_28_we; + logic ie0_0_e_29_qs; + logic ie0_0_e_29_wd; + logic ie0_0_e_29_we; + logic ie0_0_e_30_qs; + logic ie0_0_e_30_wd; + logic ie0_0_e_30_we; + logic ie0_0_e_31_qs; + logic ie0_0_e_31_wd; + logic ie0_0_e_31_we; + logic ie0_1_e_32_qs; + logic ie0_1_e_32_wd; + logic ie0_1_e_32_we; + logic ie0_1_e_33_qs; + logic ie0_1_e_33_wd; + logic ie0_1_e_33_we; + logic ie0_1_e_34_qs; + logic ie0_1_e_34_wd; + logic ie0_1_e_34_we; + logic ie0_1_e_35_qs; + logic ie0_1_e_35_wd; + logic ie0_1_e_35_we; + logic ie0_1_e_36_qs; + logic ie0_1_e_36_wd; + logic ie0_1_e_36_we; + logic ie0_1_e_37_qs; + logic ie0_1_e_37_wd; + logic ie0_1_e_37_we; + logic ie0_1_e_38_qs; + logic ie0_1_e_38_wd; + logic ie0_1_e_38_we; + logic ie0_1_e_39_qs; + logic ie0_1_e_39_wd; + logic ie0_1_e_39_we; + logic ie0_1_e_40_qs; + logic ie0_1_e_40_wd; + logic ie0_1_e_40_we; + logic ie0_1_e_41_qs; + logic ie0_1_e_41_wd; + logic ie0_1_e_41_we; + logic ie0_1_e_42_qs; + logic ie0_1_e_42_wd; + logic ie0_1_e_42_we; + logic ie0_1_e_43_qs; + logic ie0_1_e_43_wd; + logic ie0_1_e_43_we; + logic [1:0] threshold0_qs; + logic [1:0] threshold0_wd; + logic threshold0_we; + logic [7:0] cc0_qs; + logic [7:0] cc0_wd; + logic cc0_we; + logic cc0_re; + logic msip0_qs; + logic msip0_wd; + logic msip0_we; + + // Register instances + + // Subregister 0 of Multireg ip + // R[ip_0]: V(False) + + // F[p_0]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[0].de), + .d (hw2reg.ip[0].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_0_qs) + ); + + + // F[p_1]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[1].de), + .d (hw2reg.ip[1].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_1_qs) + ); + + + // F[p_2]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[2].de), + .d (hw2reg.ip[2].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_2_qs) + ); + + + // F[p_3]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[3].de), + .d (hw2reg.ip[3].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_3_qs) + ); + + + // F[p_4]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_4 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[4].de), + .d (hw2reg.ip[4].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_4_qs) + ); + + + // F[p_5]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_5 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[5].de), + .d (hw2reg.ip[5].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_5_qs) + ); + + + // F[p_6]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_6 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[6].de), + .d (hw2reg.ip[6].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_6_qs) + ); + + + // F[p_7]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_7 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[7].de), + .d (hw2reg.ip[7].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_7_qs) + ); + + + // F[p_8]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_8 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[8].de), + .d (hw2reg.ip[8].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_8_qs) + ); + + + // F[p_9]: 9:9 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_9 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[9].de), + .d (hw2reg.ip[9].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_9_qs) + ); + + + // F[p_10]: 10:10 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_10 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[10].de), + .d (hw2reg.ip[10].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_10_qs) + ); + + + // F[p_11]: 11:11 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_11 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[11].de), + .d (hw2reg.ip[11].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_11_qs) + ); + + + // F[p_12]: 12:12 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_12 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[12].de), + .d (hw2reg.ip[12].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_12_qs) + ); + + + // F[p_13]: 13:13 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_13 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[13].de), + .d (hw2reg.ip[13].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_13_qs) + ); + + + // F[p_14]: 14:14 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_14 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[14].de), + .d (hw2reg.ip[14].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_14_qs) + ); + + + // F[p_15]: 15:15 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_15 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[15].de), + .d (hw2reg.ip[15].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_15_qs) + ); + + + // F[p_16]: 16:16 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_16 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[16].de), + .d (hw2reg.ip[16].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_16_qs) + ); + + + // F[p_17]: 17:17 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_17 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[17].de), + .d (hw2reg.ip[17].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_17_qs) + ); + + + // F[p_18]: 18:18 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_18 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[18].de), + .d (hw2reg.ip[18].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_18_qs) + ); + + + // F[p_19]: 19:19 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_19 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[19].de), + .d (hw2reg.ip[19].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_19_qs) + ); + + + // F[p_20]: 20:20 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_20 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[20].de), + .d (hw2reg.ip[20].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_20_qs) + ); + + + // F[p_21]: 21:21 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_21 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[21].de), + .d (hw2reg.ip[21].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_21_qs) + ); + + + // F[p_22]: 22:22 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_22 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[22].de), + .d (hw2reg.ip[22].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_22_qs) + ); + + + // F[p_23]: 23:23 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_23 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[23].de), + .d (hw2reg.ip[23].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_23_qs) + ); + + + // F[p_24]: 24:24 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_24 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[24].de), + .d (hw2reg.ip[24].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_24_qs) + ); + + + // F[p_25]: 25:25 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_25 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[25].de), + .d (hw2reg.ip[25].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_25_qs) + ); + + + // F[p_26]: 26:26 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_26 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[26].de), + .d (hw2reg.ip[26].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_26_qs) + ); + + + // F[p_27]: 27:27 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_27 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[27].de), + .d (hw2reg.ip[27].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_27_qs) + ); + + + // F[p_28]: 28:28 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_28 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[28].de), + .d (hw2reg.ip[28].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_28_qs) + ); + + + // F[p_29]: 29:29 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_29 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[29].de), + .d (hw2reg.ip[29].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_29_qs) + ); + + + // F[p_30]: 30:30 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_30 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[30].de), + .d (hw2reg.ip[30].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_30_qs) + ); + + + // F[p_31]: 31:31 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_0_p_31 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[31].de), + .d (hw2reg.ip[31].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_0_p_31_qs) + ); + + + // Subregister 32 of Multireg ip + // R[ip_1]: V(False) + + // F[p_32]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_32 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[32].de), + .d (hw2reg.ip[32].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_32_qs) + ); + + + // F[p_33]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_33 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[33].de), + .d (hw2reg.ip[33].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_33_qs) + ); + + + // F[p_34]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_34 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[34].de), + .d (hw2reg.ip[34].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_34_qs) + ); + + + // F[p_35]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_35 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[35].de), + .d (hw2reg.ip[35].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_35_qs) + ); + + + // F[p_36]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_36 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[36].de), + .d (hw2reg.ip[36].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_36_qs) + ); + + + // F[p_37]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_37 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[37].de), + .d (hw2reg.ip[37].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_37_qs) + ); + + + // F[p_38]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_38 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[38].de), + .d (hw2reg.ip[38].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_38_qs) + ); + + + // F[p_39]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_39 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[39].de), + .d (hw2reg.ip[39].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_39_qs) + ); + + + // F[p_40]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_40 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[40].de), + .d (hw2reg.ip[40].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_40_qs) + ); + + + // F[p_41]: 9:9 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_41 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[41].de), + .d (hw2reg.ip[41].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_41_qs) + ); + + + // F[p_42]: 10:10 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_42 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[42].de), + .d (hw2reg.ip[42].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_42_qs) + ); + + + // F[p_43]: 11:11 + prim_subreg #( + .DW (1), + .SWACCESS("RO"), + .RESVAL (1'h0) + ) u_ip_1_p_43 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + .we (1'b0), + .wd ('0 ), + + // from internal hardware + .de (hw2reg.ip[43].de), + .d (hw2reg.ip[43].d ), + + // to internal hardware + .qe (), + .q (), + + // to register interface (read) + .qs (ip_1_p_43_qs) + ); + + // Subregister 0 of Multireg le + // R[le_0]: V(False) + + // F[le_0]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_0_we), + .wd (le_0_le_0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[0].q ), + + // to register interface (read) + .qs (le_0_le_0_qs) + ); + + + // F[le_1]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_1_we), + .wd (le_0_le_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[1].q ), + + // to register interface (read) + .qs (le_0_le_1_qs) + ); + + + // F[le_2]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_2_we), + .wd (le_0_le_2_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[2].q ), + + // to register interface (read) + .qs (le_0_le_2_qs) + ); + + + // F[le_3]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_3_we), + .wd (le_0_le_3_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[3].q ), + + // to register interface (read) + .qs (le_0_le_3_qs) + ); + + + // F[le_4]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_4 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_4_we), + .wd (le_0_le_4_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[4].q ), + + // to register interface (read) + .qs (le_0_le_4_qs) + ); + + + // F[le_5]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_5 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_5_we), + .wd (le_0_le_5_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[5].q ), + + // to register interface (read) + .qs (le_0_le_5_qs) + ); + + + // F[le_6]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_6 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_6_we), + .wd (le_0_le_6_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[6].q ), + + // to register interface (read) + .qs (le_0_le_6_qs) + ); + + + // F[le_7]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_7 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_7_we), + .wd (le_0_le_7_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[7].q ), + + // to register interface (read) + .qs (le_0_le_7_qs) + ); + + + // F[le_8]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_8 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_8_we), + .wd (le_0_le_8_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[8].q ), + + // to register interface (read) + .qs (le_0_le_8_qs) + ); + + + // F[le_9]: 9:9 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_9 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_9_we), + .wd (le_0_le_9_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[9].q ), + + // to register interface (read) + .qs (le_0_le_9_qs) + ); + + + // F[le_10]: 10:10 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_10 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_10_we), + .wd (le_0_le_10_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[10].q ), + + // to register interface (read) + .qs (le_0_le_10_qs) + ); + + + // F[le_11]: 11:11 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_11 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_11_we), + .wd (le_0_le_11_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[11].q ), + + // to register interface (read) + .qs (le_0_le_11_qs) + ); + + + // F[le_12]: 12:12 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_12 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_12_we), + .wd (le_0_le_12_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[12].q ), + + // to register interface (read) + .qs (le_0_le_12_qs) + ); + + + // F[le_13]: 13:13 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_13 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_13_we), + .wd (le_0_le_13_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[13].q ), + + // to register interface (read) + .qs (le_0_le_13_qs) + ); + + + // F[le_14]: 14:14 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_14 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_14_we), + .wd (le_0_le_14_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[14].q ), + + // to register interface (read) + .qs (le_0_le_14_qs) + ); + + + // F[le_15]: 15:15 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_15 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_15_we), + .wd (le_0_le_15_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[15].q ), + + // to register interface (read) + .qs (le_0_le_15_qs) + ); + + + // F[le_16]: 16:16 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_16 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_16_we), + .wd (le_0_le_16_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[16].q ), + + // to register interface (read) + .qs (le_0_le_16_qs) + ); + + + // F[le_17]: 17:17 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_17 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_17_we), + .wd (le_0_le_17_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[17].q ), + + // to register interface (read) + .qs (le_0_le_17_qs) + ); + + + // F[le_18]: 18:18 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_18 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_18_we), + .wd (le_0_le_18_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[18].q ), + + // to register interface (read) + .qs (le_0_le_18_qs) + ); + + + // F[le_19]: 19:19 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_19 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_19_we), + .wd (le_0_le_19_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[19].q ), + + // to register interface (read) + .qs (le_0_le_19_qs) + ); + + + // F[le_20]: 20:20 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_20 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_20_we), + .wd (le_0_le_20_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[20].q ), + + // to register interface (read) + .qs (le_0_le_20_qs) + ); + + + // F[le_21]: 21:21 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_21 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_21_we), + .wd (le_0_le_21_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[21].q ), + + // to register interface (read) + .qs (le_0_le_21_qs) + ); + + + // F[le_22]: 22:22 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_22 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_22_we), + .wd (le_0_le_22_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[22].q ), + + // to register interface (read) + .qs (le_0_le_22_qs) + ); + + + // F[le_23]: 23:23 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_23 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_23_we), + .wd (le_0_le_23_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[23].q ), + + // to register interface (read) + .qs (le_0_le_23_qs) + ); + + + // F[le_24]: 24:24 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_24 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_24_we), + .wd (le_0_le_24_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[24].q ), + + // to register interface (read) + .qs (le_0_le_24_qs) + ); + + + // F[le_25]: 25:25 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_25 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_25_we), + .wd (le_0_le_25_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[25].q ), + + // to register interface (read) + .qs (le_0_le_25_qs) + ); + + + // F[le_26]: 26:26 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_26 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_26_we), + .wd (le_0_le_26_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[26].q ), + + // to register interface (read) + .qs (le_0_le_26_qs) + ); + + + // F[le_27]: 27:27 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_27 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_27_we), + .wd (le_0_le_27_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[27].q ), + + // to register interface (read) + .qs (le_0_le_27_qs) + ); + + + // F[le_28]: 28:28 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_28 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_28_we), + .wd (le_0_le_28_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[28].q ), + + // to register interface (read) + .qs (le_0_le_28_qs) + ); + + + // F[le_29]: 29:29 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_29 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_29_we), + .wd (le_0_le_29_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[29].q ), + + // to register interface (read) + .qs (le_0_le_29_qs) + ); + + + // F[le_30]: 30:30 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_30 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_30_we), + .wd (le_0_le_30_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[30].q ), + + // to register interface (read) + .qs (le_0_le_30_qs) + ); + + + // F[le_31]: 31:31 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_0_le_31 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_0_le_31_we), + .wd (le_0_le_31_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[31].q ), + + // to register interface (read) + .qs (le_0_le_31_qs) + ); + + + // Subregister 32 of Multireg le + // R[le_1]: V(False) + + // F[le_32]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_32 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_32_we), + .wd (le_1_le_32_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[32].q ), + + // to register interface (read) + .qs (le_1_le_32_qs) + ); + + + // F[le_33]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_33 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_33_we), + .wd (le_1_le_33_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[33].q ), + + // to register interface (read) + .qs (le_1_le_33_qs) + ); + + + // F[le_34]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_34 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_34_we), + .wd (le_1_le_34_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[34].q ), + + // to register interface (read) + .qs (le_1_le_34_qs) + ); + + + // F[le_35]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_35 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_35_we), + .wd (le_1_le_35_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[35].q ), + + // to register interface (read) + .qs (le_1_le_35_qs) + ); + + + // F[le_36]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_36 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_36_we), + .wd (le_1_le_36_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[36].q ), + + // to register interface (read) + .qs (le_1_le_36_qs) + ); + + + // F[le_37]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_37 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_37_we), + .wd (le_1_le_37_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[37].q ), + + // to register interface (read) + .qs (le_1_le_37_qs) + ); + + + // F[le_38]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_38 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_38_we), + .wd (le_1_le_38_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[38].q ), + + // to register interface (read) + .qs (le_1_le_38_qs) + ); + + + // F[le_39]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_39 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_39_we), + .wd (le_1_le_39_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[39].q ), + + // to register interface (read) + .qs (le_1_le_39_qs) + ); + + + // F[le_40]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_40 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_40_we), + .wd (le_1_le_40_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[40].q ), + + // to register interface (read) + .qs (le_1_le_40_qs) + ); + + + // F[le_41]: 9:9 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_41 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_41_we), + .wd (le_1_le_41_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[41].q ), + + // to register interface (read) + .qs (le_1_le_41_qs) + ); + + + // F[le_42]: 10:10 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_42 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_42_we), + .wd (le_1_le_42_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[42].q ), + + // to register interface (read) + .qs (le_1_le_42_qs) + ); + + + // F[le_43]: 11:11 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_le_1_le_43 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (le_1_le_43_we), + .wd (le_1_le_43_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.le[43].q ), + + // to register interface (read) + .qs (le_1_le_43_qs) + ); + + // R[prio0]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio0_we), + .wd (prio0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio0.q ), + + // to register interface (read) + .qs (prio0_qs) + ); + + + // R[prio1]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio1_we), + .wd (prio1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio1.q ), + + // to register interface (read) + .qs (prio1_qs) + ); + + + // R[prio2]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio2_we), + .wd (prio2_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio2.q ), + + // to register interface (read) + .qs (prio2_qs) + ); + + + // R[prio3]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio3_we), + .wd (prio3_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio3.q ), + + // to register interface (read) + .qs (prio3_qs) + ); + + + // R[prio4]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio4 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio4_we), + .wd (prio4_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio4.q ), + + // to register interface (read) + .qs (prio4_qs) + ); + + + // R[prio5]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio5 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio5_we), + .wd (prio5_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio5.q ), + + // to register interface (read) + .qs (prio5_qs) + ); + + + // R[prio6]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio6 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio6_we), + .wd (prio6_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio6.q ), + + // to register interface (read) + .qs (prio6_qs) + ); + + + // R[prio7]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio7 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio7_we), + .wd (prio7_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio7.q ), + + // to register interface (read) + .qs (prio7_qs) + ); + + + // R[prio8]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio8 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio8_we), + .wd (prio8_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio8.q ), + + // to register interface (read) + .qs (prio8_qs) + ); + + + // R[prio9]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio9 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio9_we), + .wd (prio9_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio9.q ), + + // to register interface (read) + .qs (prio9_qs) + ); + + + // R[prio10]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio10 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio10_we), + .wd (prio10_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio10.q ), + + // to register interface (read) + .qs (prio10_qs) + ); + + + // R[prio11]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio11 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio11_we), + .wd (prio11_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio11.q ), + + // to register interface (read) + .qs (prio11_qs) + ); + + + // R[prio12]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio12 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio12_we), + .wd (prio12_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio12.q ), + + // to register interface (read) + .qs (prio12_qs) + ); + + + // R[prio13]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio13 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio13_we), + .wd (prio13_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio13.q ), + + // to register interface (read) + .qs (prio13_qs) + ); + + + // R[prio14]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio14 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio14_we), + .wd (prio14_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio14.q ), + + // to register interface (read) + .qs (prio14_qs) + ); + + + // R[prio15]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio15 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio15_we), + .wd (prio15_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio15.q ), + + // to register interface (read) + .qs (prio15_qs) + ); + + + // R[prio16]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio16 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio16_we), + .wd (prio16_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio16.q ), + + // to register interface (read) + .qs (prio16_qs) + ); + + + // R[prio17]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio17 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio17_we), + .wd (prio17_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio17.q ), + + // to register interface (read) + .qs (prio17_qs) + ); + + + // R[prio18]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio18 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio18_we), + .wd (prio18_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio18.q ), + + // to register interface (read) + .qs (prio18_qs) + ); + + + // R[prio19]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio19 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio19_we), + .wd (prio19_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio19.q ), + + // to register interface (read) + .qs (prio19_qs) + ); + + + // R[prio20]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio20 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio20_we), + .wd (prio20_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio20.q ), + + // to register interface (read) + .qs (prio20_qs) + ); + + + // R[prio21]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio21 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio21_we), + .wd (prio21_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio21.q ), + + // to register interface (read) + .qs (prio21_qs) + ); + + + // R[prio22]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio22 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio22_we), + .wd (prio22_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio22.q ), + + // to register interface (read) + .qs (prio22_qs) + ); + + + // R[prio23]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio23 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio23_we), + .wd (prio23_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio23.q ), + + // to register interface (read) + .qs (prio23_qs) + ); + + + // R[prio24]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio24 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio24_we), + .wd (prio24_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio24.q ), + + // to register interface (read) + .qs (prio24_qs) + ); + + + // R[prio25]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio25 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio25_we), + .wd (prio25_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio25.q ), + + // to register interface (read) + .qs (prio25_qs) + ); + + + // R[prio26]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio26 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio26_we), + .wd (prio26_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio26.q ), + + // to register interface (read) + .qs (prio26_qs) + ); + + + // R[prio27]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio27 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio27_we), + .wd (prio27_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio27.q ), + + // to register interface (read) + .qs (prio27_qs) + ); + + + // R[prio28]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio28 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio28_we), + .wd (prio28_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio28.q ), + + // to register interface (read) + .qs (prio28_qs) + ); + + + // R[prio29]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio29 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio29_we), + .wd (prio29_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio29.q ), + + // to register interface (read) + .qs (prio29_qs) + ); + + + // R[prio30]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio30 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio30_we), + .wd (prio30_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio30.q ), + + // to register interface (read) + .qs (prio30_qs) + ); + + + // R[prio31]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio31 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio31_we), + .wd (prio31_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio31.q ), + + // to register interface (read) + .qs (prio31_qs) + ); + + + // R[prio32]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio32 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio32_we), + .wd (prio32_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio32.q ), + + // to register interface (read) + .qs (prio32_qs) + ); + + + // R[prio33]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio33 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio33_we), + .wd (prio33_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio33.q ), + + // to register interface (read) + .qs (prio33_qs) + ); + + + // R[prio34]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio34 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio34_we), + .wd (prio34_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio34.q ), + + // to register interface (read) + .qs (prio34_qs) + ); + + + // R[prio35]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio35 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio35_we), + .wd (prio35_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio35.q ), + + // to register interface (read) + .qs (prio35_qs) + ); + + + // R[prio36]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio36 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio36_we), + .wd (prio36_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio36.q ), + + // to register interface (read) + .qs (prio36_qs) + ); + + + // R[prio37]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio37 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio37_we), + .wd (prio37_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio37.q ), + + // to register interface (read) + .qs (prio37_qs) + ); + + + // R[prio38]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio38 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio38_we), + .wd (prio38_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio38.q ), + + // to register interface (read) + .qs (prio38_qs) + ); + + + // R[prio39]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio39 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio39_we), + .wd (prio39_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio39.q ), + + // to register interface (read) + .qs (prio39_qs) + ); + + + // R[prio40]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio40 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio40_we), + .wd (prio40_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio40.q ), + + // to register interface (read) + .qs (prio40_qs) + ); + + + // R[prio41]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio41 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio41_we), + .wd (prio41_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio41.q ), + + // to register interface (read) + .qs (prio41_qs) + ); + + + // R[prio42]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio42 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio42_we), + .wd (prio42_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio42.q ), + + // to register interface (read) + .qs (prio42_qs) + ); + + + // R[prio43]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_prio43 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (prio43_we), + .wd (prio43_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.prio43.q ), + + // to register interface (read) + .qs (prio43_qs) + ); + + // Subregister 0 of Multireg ie0 + // R[ie0_0]: V(False) + + // F[e_0]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_0_we), + .wd (ie0_0_e_0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[0].q ), + + // to register interface (read) + .qs (ie0_0_e_0_qs) + ); + + + // F[e_1]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_1 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_1_we), + .wd (ie0_0_e_1_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[1].q ), + + // to register interface (read) + .qs (ie0_0_e_1_qs) + ); + + + // F[e_2]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_2 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_2_we), + .wd (ie0_0_e_2_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[2].q ), + + // to register interface (read) + .qs (ie0_0_e_2_qs) + ); + + + // F[e_3]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_3 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_3_we), + .wd (ie0_0_e_3_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[3].q ), + + // to register interface (read) + .qs (ie0_0_e_3_qs) + ); + + + // F[e_4]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_4 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_4_we), + .wd (ie0_0_e_4_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[4].q ), + + // to register interface (read) + .qs (ie0_0_e_4_qs) + ); + + + // F[e_5]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_5 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_5_we), + .wd (ie0_0_e_5_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[5].q ), + + // to register interface (read) + .qs (ie0_0_e_5_qs) + ); + + + // F[e_6]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_6 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_6_we), + .wd (ie0_0_e_6_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[6].q ), + + // to register interface (read) + .qs (ie0_0_e_6_qs) + ); + + + // F[e_7]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_7 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_7_we), + .wd (ie0_0_e_7_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[7].q ), + + // to register interface (read) + .qs (ie0_0_e_7_qs) + ); + + + // F[e_8]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_8 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_8_we), + .wd (ie0_0_e_8_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[8].q ), + + // to register interface (read) + .qs (ie0_0_e_8_qs) + ); + + + // F[e_9]: 9:9 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_9 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_9_we), + .wd (ie0_0_e_9_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[9].q ), + + // to register interface (read) + .qs (ie0_0_e_9_qs) + ); + + + // F[e_10]: 10:10 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_10 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_10_we), + .wd (ie0_0_e_10_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[10].q ), + + // to register interface (read) + .qs (ie0_0_e_10_qs) + ); + + + // F[e_11]: 11:11 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_11 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_11_we), + .wd (ie0_0_e_11_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[11].q ), + + // to register interface (read) + .qs (ie0_0_e_11_qs) + ); + + + // F[e_12]: 12:12 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_12 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_12_we), + .wd (ie0_0_e_12_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[12].q ), + + // to register interface (read) + .qs (ie0_0_e_12_qs) + ); + + + // F[e_13]: 13:13 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_13 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_13_we), + .wd (ie0_0_e_13_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[13].q ), + + // to register interface (read) + .qs (ie0_0_e_13_qs) + ); + + + // F[e_14]: 14:14 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_14 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_14_we), + .wd (ie0_0_e_14_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[14].q ), + + // to register interface (read) + .qs (ie0_0_e_14_qs) + ); + + + // F[e_15]: 15:15 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_15 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_15_we), + .wd (ie0_0_e_15_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[15].q ), + + // to register interface (read) + .qs (ie0_0_e_15_qs) + ); + + + // F[e_16]: 16:16 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_16 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_16_we), + .wd (ie0_0_e_16_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[16].q ), + + // to register interface (read) + .qs (ie0_0_e_16_qs) + ); + + + // F[e_17]: 17:17 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_17 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_17_we), + .wd (ie0_0_e_17_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[17].q ), + + // to register interface (read) + .qs (ie0_0_e_17_qs) + ); + + + // F[e_18]: 18:18 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_18 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_18_we), + .wd (ie0_0_e_18_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[18].q ), + + // to register interface (read) + .qs (ie0_0_e_18_qs) + ); + + + // F[e_19]: 19:19 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_19 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_19_we), + .wd (ie0_0_e_19_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[19].q ), + + // to register interface (read) + .qs (ie0_0_e_19_qs) + ); + + + // F[e_20]: 20:20 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_20 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_20_we), + .wd (ie0_0_e_20_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[20].q ), + + // to register interface (read) + .qs (ie0_0_e_20_qs) + ); + + + // F[e_21]: 21:21 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_21 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_21_we), + .wd (ie0_0_e_21_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[21].q ), + + // to register interface (read) + .qs (ie0_0_e_21_qs) + ); + + + // F[e_22]: 22:22 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_22 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_22_we), + .wd (ie0_0_e_22_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[22].q ), + + // to register interface (read) + .qs (ie0_0_e_22_qs) + ); + + + // F[e_23]: 23:23 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_23 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_23_we), + .wd (ie0_0_e_23_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[23].q ), + + // to register interface (read) + .qs (ie0_0_e_23_qs) + ); + + + // F[e_24]: 24:24 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_24 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_24_we), + .wd (ie0_0_e_24_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[24].q ), + + // to register interface (read) + .qs (ie0_0_e_24_qs) + ); + + + // F[e_25]: 25:25 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_25 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_25_we), + .wd (ie0_0_e_25_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[25].q ), + + // to register interface (read) + .qs (ie0_0_e_25_qs) + ); + + + // F[e_26]: 26:26 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_26 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_26_we), + .wd (ie0_0_e_26_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[26].q ), + + // to register interface (read) + .qs (ie0_0_e_26_qs) + ); + + + // F[e_27]: 27:27 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_27 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_27_we), + .wd (ie0_0_e_27_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[27].q ), + + // to register interface (read) + .qs (ie0_0_e_27_qs) + ); + + + // F[e_28]: 28:28 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_28 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_28_we), + .wd (ie0_0_e_28_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[28].q ), + + // to register interface (read) + .qs (ie0_0_e_28_qs) + ); + + + // F[e_29]: 29:29 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_29 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_29_we), + .wd (ie0_0_e_29_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[29].q ), + + // to register interface (read) + .qs (ie0_0_e_29_qs) + ); + + + // F[e_30]: 30:30 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_30 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_30_we), + .wd (ie0_0_e_30_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[30].q ), + + // to register interface (read) + .qs (ie0_0_e_30_qs) + ); + + + // F[e_31]: 31:31 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_0_e_31 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_0_e_31_we), + .wd (ie0_0_e_31_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[31].q ), + + // to register interface (read) + .qs (ie0_0_e_31_qs) + ); + + + // Subregister 32 of Multireg ie0 + // R[ie0_1]: V(False) + + // F[e_32]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_32 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_32_we), + .wd (ie0_1_e_32_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[32].q ), + + // to register interface (read) + .qs (ie0_1_e_32_qs) + ); + + + // F[e_33]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_33 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_33_we), + .wd (ie0_1_e_33_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[33].q ), + + // to register interface (read) + .qs (ie0_1_e_33_qs) + ); + + + // F[e_34]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_34 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_34_we), + .wd (ie0_1_e_34_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[34].q ), + + // to register interface (read) + .qs (ie0_1_e_34_qs) + ); + + + // F[e_35]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_35 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_35_we), + .wd (ie0_1_e_35_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[35].q ), + + // to register interface (read) + .qs (ie0_1_e_35_qs) + ); + + + // F[e_36]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_36 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_36_we), + .wd (ie0_1_e_36_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[36].q ), + + // to register interface (read) + .qs (ie0_1_e_36_qs) + ); + + + // F[e_37]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_37 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_37_we), + .wd (ie0_1_e_37_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[37].q ), + + // to register interface (read) + .qs (ie0_1_e_37_qs) + ); + + + // F[e_38]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_38 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_38_we), + .wd (ie0_1_e_38_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[38].q ), + + // to register interface (read) + .qs (ie0_1_e_38_qs) + ); + + + // F[e_39]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_39 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_39_we), + .wd (ie0_1_e_39_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[39].q ), + + // to register interface (read) + .qs (ie0_1_e_39_qs) + ); + + + // F[e_40]: 8:8 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_40 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_40_we), + .wd (ie0_1_e_40_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[40].q ), + + // to register interface (read) + .qs (ie0_1_e_40_qs) + ); + + + // F[e_41]: 9:9 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_41 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_41_we), + .wd (ie0_1_e_41_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[41].q ), + + // to register interface (read) + .qs (ie0_1_e_41_qs) + ); + + + // F[e_42]: 10:10 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_42 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_42_we), + .wd (ie0_1_e_42_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[42].q ), + + // to register interface (read) + .qs (ie0_1_e_42_qs) + ); + + + // F[e_43]: 11:11 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ie0_1_e_43 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ie0_1_e_43_we), + .wd (ie0_1_e_43_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ie0[43].q ), + + // to register interface (read) + .qs (ie0_1_e_43_qs) + ); + + // R[threshold0]: V(False) + + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_threshold0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (threshold0_we), + .wd (threshold0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.threshold0.q ), + + // to register interface (read) + .qs (threshold0_qs) + ); + + + // R[cc0]: V(True) + + prim_subreg_ext #( + .DW (8) + ) u_cc0 ( + .re (cc0_re), + .we (cc0_we), + .wd (cc0_wd), + .d (hw2reg.cc0.d), + .qre (reg2hw.cc0.re), + .qe (reg2hw.cc0.qe), + .q (reg2hw.cc0.q ), + .qs (cc0_qs) + ); + + + // R[msip0]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_msip0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (msip0_we), + .wd (msip0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.msip0.q ), + + // to register interface (read) + .qs (msip0_qs) + ); + + + + + logic [52:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[ 0] = (reg_addr == RV_PLIC_IP_0_OFFSET); + addr_hit[ 1] = (reg_addr == RV_PLIC_IP_1_OFFSET); + addr_hit[ 2] = (reg_addr == RV_PLIC_LE_0_OFFSET); + addr_hit[ 3] = (reg_addr == RV_PLIC_LE_1_OFFSET); + addr_hit[ 4] = (reg_addr == RV_PLIC_PRIO0_OFFSET); + addr_hit[ 5] = (reg_addr == RV_PLIC_PRIO1_OFFSET); + addr_hit[ 6] = (reg_addr == RV_PLIC_PRIO2_OFFSET); + addr_hit[ 7] = (reg_addr == RV_PLIC_PRIO3_OFFSET); + addr_hit[ 8] = (reg_addr == RV_PLIC_PRIO4_OFFSET); + addr_hit[ 9] = (reg_addr == RV_PLIC_PRIO5_OFFSET); + addr_hit[ 10] = (reg_addr == RV_PLIC_PRIO6_OFFSET); + addr_hit[ 11] = (reg_addr == RV_PLIC_PRIO7_OFFSET); + addr_hit[ 12] = (reg_addr == RV_PLIC_PRIO8_OFFSET); + addr_hit[ 13] = (reg_addr == RV_PLIC_PRIO9_OFFSET); + addr_hit[ 14] = (reg_addr == RV_PLIC_PRIO10_OFFSET); + addr_hit[ 15] = (reg_addr == RV_PLIC_PRIO11_OFFSET); + addr_hit[ 16] = (reg_addr == RV_PLIC_PRIO12_OFFSET); + addr_hit[ 17] = (reg_addr == RV_PLIC_PRIO13_OFFSET); + addr_hit[ 18] = (reg_addr == RV_PLIC_PRIO14_OFFSET); + addr_hit[ 19] = (reg_addr == RV_PLIC_PRIO15_OFFSET); + addr_hit[ 20] = (reg_addr == RV_PLIC_PRIO16_OFFSET); + addr_hit[ 21] = (reg_addr == RV_PLIC_PRIO17_OFFSET); + addr_hit[ 22] = (reg_addr == RV_PLIC_PRIO18_OFFSET); + addr_hit[ 23] = (reg_addr == RV_PLIC_PRIO19_OFFSET); + addr_hit[ 24] = (reg_addr == RV_PLIC_PRIO20_OFFSET); + addr_hit[ 25] = (reg_addr == RV_PLIC_PRIO21_OFFSET); + addr_hit[ 26] = (reg_addr == RV_PLIC_PRIO22_OFFSET); + addr_hit[ 27] = (reg_addr == RV_PLIC_PRIO23_OFFSET); + addr_hit[ 28] = (reg_addr == RV_PLIC_PRIO24_OFFSET); + addr_hit[ 29] = (reg_addr == RV_PLIC_PRIO25_OFFSET); + addr_hit[ 30] = (reg_addr == RV_PLIC_PRIO26_OFFSET); + addr_hit[ 31] = (reg_addr == RV_PLIC_PRIO27_OFFSET); + addr_hit[ 32] = (reg_addr == RV_PLIC_PRIO28_OFFSET); + addr_hit[ 33] = (reg_addr == RV_PLIC_PRIO29_OFFSET); + addr_hit[ 34] = (reg_addr == RV_PLIC_PRIO30_OFFSET); + addr_hit[ 35] = (reg_addr == RV_PLIC_PRIO31_OFFSET); + addr_hit[ 36] = (reg_addr == RV_PLIC_PRIO32_OFFSET); + addr_hit[ 37] = (reg_addr == RV_PLIC_PRIO33_OFFSET); + addr_hit[ 38] = (reg_addr == RV_PLIC_PRIO34_OFFSET); + addr_hit[ 39] = (reg_addr == RV_PLIC_PRIO35_OFFSET); + addr_hit[ 40] = (reg_addr == RV_PLIC_PRIO36_OFFSET); + addr_hit[ 41] = (reg_addr == RV_PLIC_PRIO37_OFFSET); + addr_hit[ 42] = (reg_addr == RV_PLIC_PRIO38_OFFSET); + addr_hit[ 43] = (reg_addr == RV_PLIC_PRIO39_OFFSET); + addr_hit[ 44] = (reg_addr == RV_PLIC_PRIO40_OFFSET); + addr_hit[ 45] = (reg_addr == RV_PLIC_PRIO41_OFFSET); + addr_hit[ 46] = (reg_addr == RV_PLIC_PRIO42_OFFSET); + addr_hit[ 47] = (reg_addr == RV_PLIC_PRIO43_OFFSET); + addr_hit[ 48] = (reg_addr == RV_PLIC_IE0_0_OFFSET); + addr_hit[ 49] = (reg_addr == RV_PLIC_IE0_1_OFFSET); + addr_hit[ 50] = (reg_addr == RV_PLIC_THRESHOLD0_OFFSET); + addr_hit[ 51] = (reg_addr == RV_PLIC_CC0_OFFSET); + addr_hit[ 52] = (reg_addr == RV_PLIC_MSIP0_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = 1'b0; + if (addr_hit[ 0] && reg_we && (RV_PLIC_PERMIT[ 0] != (RV_PLIC_PERMIT[ 0] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 1] && reg_we && (RV_PLIC_PERMIT[ 1] != (RV_PLIC_PERMIT[ 1] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 2] && reg_we && (RV_PLIC_PERMIT[ 2] != (RV_PLIC_PERMIT[ 2] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 3] && reg_we && (RV_PLIC_PERMIT[ 3] != (RV_PLIC_PERMIT[ 3] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 4] && reg_we && (RV_PLIC_PERMIT[ 4] != (RV_PLIC_PERMIT[ 4] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 5] && reg_we && (RV_PLIC_PERMIT[ 5] != (RV_PLIC_PERMIT[ 5] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 6] && reg_we && (RV_PLIC_PERMIT[ 6] != (RV_PLIC_PERMIT[ 6] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 7] && reg_we && (RV_PLIC_PERMIT[ 7] != (RV_PLIC_PERMIT[ 7] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 8] && reg_we && (RV_PLIC_PERMIT[ 8] != (RV_PLIC_PERMIT[ 8] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 9] && reg_we && (RV_PLIC_PERMIT[ 9] != (RV_PLIC_PERMIT[ 9] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 10] && reg_we && (RV_PLIC_PERMIT[ 10] != (RV_PLIC_PERMIT[ 10] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 11] && reg_we && (RV_PLIC_PERMIT[ 11] != (RV_PLIC_PERMIT[ 11] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 12] && reg_we && (RV_PLIC_PERMIT[ 12] != (RV_PLIC_PERMIT[ 12] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 13] && reg_we && (RV_PLIC_PERMIT[ 13] != (RV_PLIC_PERMIT[ 13] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 14] && reg_we && (RV_PLIC_PERMIT[ 14] != (RV_PLIC_PERMIT[ 14] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 15] && reg_we && (RV_PLIC_PERMIT[ 15] != (RV_PLIC_PERMIT[ 15] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 16] && reg_we && (RV_PLIC_PERMIT[ 16] != (RV_PLIC_PERMIT[ 16] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 17] && reg_we && (RV_PLIC_PERMIT[ 17] != (RV_PLIC_PERMIT[ 17] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 18] && reg_we && (RV_PLIC_PERMIT[ 18] != (RV_PLIC_PERMIT[ 18] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 19] && reg_we && (RV_PLIC_PERMIT[ 19] != (RV_PLIC_PERMIT[ 19] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 20] && reg_we && (RV_PLIC_PERMIT[ 20] != (RV_PLIC_PERMIT[ 20] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 21] && reg_we && (RV_PLIC_PERMIT[ 21] != (RV_PLIC_PERMIT[ 21] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 22] && reg_we && (RV_PLIC_PERMIT[ 22] != (RV_PLIC_PERMIT[ 22] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 23] && reg_we && (RV_PLIC_PERMIT[ 23] != (RV_PLIC_PERMIT[ 23] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 24] && reg_we && (RV_PLIC_PERMIT[ 24] != (RV_PLIC_PERMIT[ 24] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 25] && reg_we && (RV_PLIC_PERMIT[ 25] != (RV_PLIC_PERMIT[ 25] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 26] && reg_we && (RV_PLIC_PERMIT[ 26] != (RV_PLIC_PERMIT[ 26] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 27] && reg_we && (RV_PLIC_PERMIT[ 27] != (RV_PLIC_PERMIT[ 27] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 28] && reg_we && (RV_PLIC_PERMIT[ 28] != (RV_PLIC_PERMIT[ 28] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 29] && reg_we && (RV_PLIC_PERMIT[ 29] != (RV_PLIC_PERMIT[ 29] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 30] && reg_we && (RV_PLIC_PERMIT[ 30] != (RV_PLIC_PERMIT[ 30] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 31] && reg_we && (RV_PLIC_PERMIT[ 31] != (RV_PLIC_PERMIT[ 31] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 32] && reg_we && (RV_PLIC_PERMIT[ 32] != (RV_PLIC_PERMIT[ 32] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 33] && reg_we && (RV_PLIC_PERMIT[ 33] != (RV_PLIC_PERMIT[ 33] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 34] && reg_we && (RV_PLIC_PERMIT[ 34] != (RV_PLIC_PERMIT[ 34] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 35] && reg_we && (RV_PLIC_PERMIT[ 35] != (RV_PLIC_PERMIT[ 35] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 36] && reg_we && (RV_PLIC_PERMIT[ 36] != (RV_PLIC_PERMIT[ 36] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 37] && reg_we && (RV_PLIC_PERMIT[ 37] != (RV_PLIC_PERMIT[ 37] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 38] && reg_we && (RV_PLIC_PERMIT[ 38] != (RV_PLIC_PERMIT[ 38] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 39] && reg_we && (RV_PLIC_PERMIT[ 39] != (RV_PLIC_PERMIT[ 39] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 40] && reg_we && (RV_PLIC_PERMIT[ 40] != (RV_PLIC_PERMIT[ 40] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 41] && reg_we && (RV_PLIC_PERMIT[ 41] != (RV_PLIC_PERMIT[ 41] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 42] && reg_we && (RV_PLIC_PERMIT[ 42] != (RV_PLIC_PERMIT[ 42] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 43] && reg_we && (RV_PLIC_PERMIT[ 43] != (RV_PLIC_PERMIT[ 43] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 44] && reg_we && (RV_PLIC_PERMIT[ 44] != (RV_PLIC_PERMIT[ 44] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 45] && reg_we && (RV_PLIC_PERMIT[ 45] != (RV_PLIC_PERMIT[ 45] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 46] && reg_we && (RV_PLIC_PERMIT[ 46] != (RV_PLIC_PERMIT[ 46] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 47] && reg_we && (RV_PLIC_PERMIT[ 47] != (RV_PLIC_PERMIT[ 47] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 48] && reg_we && (RV_PLIC_PERMIT[ 48] != (RV_PLIC_PERMIT[ 48] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 49] && reg_we && (RV_PLIC_PERMIT[ 49] != (RV_PLIC_PERMIT[ 49] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 50] && reg_we && (RV_PLIC_PERMIT[ 50] != (RV_PLIC_PERMIT[ 50] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 51] && reg_we && (RV_PLIC_PERMIT[ 51] != (RV_PLIC_PERMIT[ 51] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 52] && reg_we && (RV_PLIC_PERMIT[ 52] != (RV_PLIC_PERMIT[ 52] & reg_be))) wr_err = 1'b1 ; + end + + + + + + + assign le_0_le_0_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_0_wd = reg_wdata[0]; + + assign le_0_le_1_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_1_wd = reg_wdata[1]; + + assign le_0_le_2_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_2_wd = reg_wdata[2]; + + assign le_0_le_3_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_3_wd = reg_wdata[3]; + + assign le_0_le_4_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_4_wd = reg_wdata[4]; + + assign le_0_le_5_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_5_wd = reg_wdata[5]; + + assign le_0_le_6_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_6_wd = reg_wdata[6]; + + assign le_0_le_7_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_7_wd = reg_wdata[7]; + + assign le_0_le_8_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_8_wd = reg_wdata[8]; + + assign le_0_le_9_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_9_wd = reg_wdata[9]; + + assign le_0_le_10_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_10_wd = reg_wdata[10]; + + assign le_0_le_11_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_11_wd = reg_wdata[11]; + + assign le_0_le_12_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_12_wd = reg_wdata[12]; + + assign le_0_le_13_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_13_wd = reg_wdata[13]; + + assign le_0_le_14_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_14_wd = reg_wdata[14]; + + assign le_0_le_15_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_15_wd = reg_wdata[15]; + + assign le_0_le_16_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_16_wd = reg_wdata[16]; + + assign le_0_le_17_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_17_wd = reg_wdata[17]; + + assign le_0_le_18_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_18_wd = reg_wdata[18]; + + assign le_0_le_19_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_19_wd = reg_wdata[19]; + + assign le_0_le_20_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_20_wd = reg_wdata[20]; + + assign le_0_le_21_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_21_wd = reg_wdata[21]; + + assign le_0_le_22_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_22_wd = reg_wdata[22]; + + assign le_0_le_23_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_23_wd = reg_wdata[23]; + + assign le_0_le_24_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_24_wd = reg_wdata[24]; + + assign le_0_le_25_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_25_wd = reg_wdata[25]; + + assign le_0_le_26_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_26_wd = reg_wdata[26]; + + assign le_0_le_27_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_27_wd = reg_wdata[27]; + + assign le_0_le_28_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_28_wd = reg_wdata[28]; + + assign le_0_le_29_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_29_wd = reg_wdata[29]; + + assign le_0_le_30_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_30_wd = reg_wdata[30]; + + assign le_0_le_31_we = addr_hit[2] & reg_we & ~wr_err; + assign le_0_le_31_wd = reg_wdata[31]; + + assign le_1_le_32_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_32_wd = reg_wdata[0]; + + assign le_1_le_33_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_33_wd = reg_wdata[1]; + + assign le_1_le_34_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_34_wd = reg_wdata[2]; + + assign le_1_le_35_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_35_wd = reg_wdata[3]; + + assign le_1_le_36_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_36_wd = reg_wdata[4]; + + assign le_1_le_37_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_37_wd = reg_wdata[5]; + + assign le_1_le_38_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_38_wd = reg_wdata[6]; + + assign le_1_le_39_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_39_wd = reg_wdata[7]; + + assign le_1_le_40_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_40_wd = reg_wdata[8]; + + assign le_1_le_41_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_41_wd = reg_wdata[9]; + + assign le_1_le_42_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_42_wd = reg_wdata[10]; + + assign le_1_le_43_we = addr_hit[3] & reg_we & ~wr_err; + assign le_1_le_43_wd = reg_wdata[11]; + + assign prio0_we = addr_hit[4] & reg_we & ~wr_err; + assign prio0_wd = reg_wdata[1:0]; + + assign prio1_we = addr_hit[5] & reg_we & ~wr_err; + assign prio1_wd = reg_wdata[1:0]; + + assign prio2_we = addr_hit[6] & reg_we & ~wr_err; + assign prio2_wd = reg_wdata[1:0]; + + assign prio3_we = addr_hit[7] & reg_we & ~wr_err; + assign prio3_wd = reg_wdata[1:0]; + + assign prio4_we = addr_hit[8] & reg_we & ~wr_err; + assign prio4_wd = reg_wdata[1:0]; + + assign prio5_we = addr_hit[9] & reg_we & ~wr_err; + assign prio5_wd = reg_wdata[1:0]; + + assign prio6_we = addr_hit[10] & reg_we & ~wr_err; + assign prio6_wd = reg_wdata[1:0]; + + assign prio7_we = addr_hit[11] & reg_we & ~wr_err; + assign prio7_wd = reg_wdata[1:0]; + + assign prio8_we = addr_hit[12] & reg_we & ~wr_err; + assign prio8_wd = reg_wdata[1:0]; + + assign prio9_we = addr_hit[13] & reg_we & ~wr_err; + assign prio9_wd = reg_wdata[1:0]; + + assign prio10_we = addr_hit[14] & reg_we & ~wr_err; + assign prio10_wd = reg_wdata[1:0]; + + assign prio11_we = addr_hit[15] & reg_we & ~wr_err; + assign prio11_wd = reg_wdata[1:0]; + + assign prio12_we = addr_hit[16] & reg_we & ~wr_err; + assign prio12_wd = reg_wdata[1:0]; + + assign prio13_we = addr_hit[17] & reg_we & ~wr_err; + assign prio13_wd = reg_wdata[1:0]; + + assign prio14_we = addr_hit[18] & reg_we & ~wr_err; + assign prio14_wd = reg_wdata[1:0]; + + assign prio15_we = addr_hit[19] & reg_we & ~wr_err; + assign prio15_wd = reg_wdata[1:0]; + + assign prio16_we = addr_hit[20] & reg_we & ~wr_err; + assign prio16_wd = reg_wdata[1:0]; + + assign prio17_we = addr_hit[21] & reg_we & ~wr_err; + assign prio17_wd = reg_wdata[1:0]; + + assign prio18_we = addr_hit[22] & reg_we & ~wr_err; + assign prio18_wd = reg_wdata[1:0]; + + assign prio19_we = addr_hit[23] & reg_we & ~wr_err; + assign prio19_wd = reg_wdata[1:0]; + + assign prio20_we = addr_hit[24] & reg_we & ~wr_err; + assign prio20_wd = reg_wdata[1:0]; + + assign prio21_we = addr_hit[25] & reg_we & ~wr_err; + assign prio21_wd = reg_wdata[1:0]; + + assign prio22_we = addr_hit[26] & reg_we & ~wr_err; + assign prio22_wd = reg_wdata[1:0]; + + assign prio23_we = addr_hit[27] & reg_we & ~wr_err; + assign prio23_wd = reg_wdata[1:0]; + + assign prio24_we = addr_hit[28] & reg_we & ~wr_err; + assign prio24_wd = reg_wdata[1:0]; + + assign prio25_we = addr_hit[29] & reg_we & ~wr_err; + assign prio25_wd = reg_wdata[1:0]; + + assign prio26_we = addr_hit[30] & reg_we & ~wr_err; + assign prio26_wd = reg_wdata[1:0]; + + assign prio27_we = addr_hit[31] & reg_we & ~wr_err; + assign prio27_wd = reg_wdata[1:0]; + + assign prio28_we = addr_hit[32] & reg_we & ~wr_err; + assign prio28_wd = reg_wdata[1:0]; + + assign prio29_we = addr_hit[33] & reg_we & ~wr_err; + assign prio29_wd = reg_wdata[1:0]; + + assign prio30_we = addr_hit[34] & reg_we & ~wr_err; + assign prio30_wd = reg_wdata[1:0]; + + assign prio31_we = addr_hit[35] & reg_we & ~wr_err; + assign prio31_wd = reg_wdata[1:0]; + + assign prio32_we = addr_hit[36] & reg_we & ~wr_err; + assign prio32_wd = reg_wdata[1:0]; + + assign prio33_we = addr_hit[37] & reg_we & ~wr_err; + assign prio33_wd = reg_wdata[1:0]; + + assign prio34_we = addr_hit[38] & reg_we & ~wr_err; + assign prio34_wd = reg_wdata[1:0]; + + assign prio35_we = addr_hit[39] & reg_we & ~wr_err; + assign prio35_wd = reg_wdata[1:0]; + + assign prio36_we = addr_hit[40] & reg_we & ~wr_err; + assign prio36_wd = reg_wdata[1:0]; + + assign prio37_we = addr_hit[41] & reg_we & ~wr_err; + assign prio37_wd = reg_wdata[1:0]; + + assign prio38_we = addr_hit[42] & reg_we & ~wr_err; + assign prio38_wd = reg_wdata[1:0]; + + assign prio39_we = addr_hit[43] & reg_we & ~wr_err; + assign prio39_wd = reg_wdata[1:0]; + + assign prio40_we = addr_hit[44] & reg_we & ~wr_err; + assign prio40_wd = reg_wdata[1:0]; + + assign prio41_we = addr_hit[45] & reg_we & ~wr_err; + assign prio41_wd = reg_wdata[1:0]; + + assign prio42_we = addr_hit[46] & reg_we & ~wr_err; + assign prio42_wd = reg_wdata[1:0]; + + assign prio43_we = addr_hit[47] & reg_we & ~wr_err; + assign prio43_wd = reg_wdata[1:0]; + + assign ie0_0_e_0_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_0_wd = reg_wdata[0]; + + assign ie0_0_e_1_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_1_wd = reg_wdata[1]; + + assign ie0_0_e_2_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_2_wd = reg_wdata[2]; + + assign ie0_0_e_3_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_3_wd = reg_wdata[3]; + + assign ie0_0_e_4_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_4_wd = reg_wdata[4]; + + assign ie0_0_e_5_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_5_wd = reg_wdata[5]; + + assign ie0_0_e_6_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_6_wd = reg_wdata[6]; + + assign ie0_0_e_7_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_7_wd = reg_wdata[7]; + + assign ie0_0_e_8_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_8_wd = reg_wdata[8]; + + assign ie0_0_e_9_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_9_wd = reg_wdata[9]; + + assign ie0_0_e_10_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_10_wd = reg_wdata[10]; + + assign ie0_0_e_11_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_11_wd = reg_wdata[11]; + + assign ie0_0_e_12_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_12_wd = reg_wdata[12]; + + assign ie0_0_e_13_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_13_wd = reg_wdata[13]; + + assign ie0_0_e_14_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_14_wd = reg_wdata[14]; + + assign ie0_0_e_15_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_15_wd = reg_wdata[15]; + + assign ie0_0_e_16_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_16_wd = reg_wdata[16]; + + assign ie0_0_e_17_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_17_wd = reg_wdata[17]; + + assign ie0_0_e_18_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_18_wd = reg_wdata[18]; + + assign ie0_0_e_19_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_19_wd = reg_wdata[19]; + + assign ie0_0_e_20_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_20_wd = reg_wdata[20]; + + assign ie0_0_e_21_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_21_wd = reg_wdata[21]; + + assign ie0_0_e_22_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_22_wd = reg_wdata[22]; + + assign ie0_0_e_23_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_23_wd = reg_wdata[23]; + + assign ie0_0_e_24_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_24_wd = reg_wdata[24]; + + assign ie0_0_e_25_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_25_wd = reg_wdata[25]; + + assign ie0_0_e_26_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_26_wd = reg_wdata[26]; + + assign ie0_0_e_27_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_27_wd = reg_wdata[27]; + + assign ie0_0_e_28_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_28_wd = reg_wdata[28]; + + assign ie0_0_e_29_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_29_wd = reg_wdata[29]; + + assign ie0_0_e_30_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_30_wd = reg_wdata[30]; + + assign ie0_0_e_31_we = addr_hit[48] & reg_we & ~wr_err; + assign ie0_0_e_31_wd = reg_wdata[31]; + + assign ie0_1_e_32_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_32_wd = reg_wdata[0]; + + assign ie0_1_e_33_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_33_wd = reg_wdata[1]; + + assign ie0_1_e_34_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_34_wd = reg_wdata[2]; + + assign ie0_1_e_35_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_35_wd = reg_wdata[3]; + + assign ie0_1_e_36_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_36_wd = reg_wdata[4]; + + assign ie0_1_e_37_we = addr_hit[6] & reg_we & ~wr_err; + assign ie0_1_e_37_wd = reg_wdata[5]; + + assign ie0_1_e_38_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_38_wd = reg_wdata[6]; + + assign ie0_1_e_39_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_39_wd = reg_wdata[7]; + + assign ie0_1_e_40_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_40_wd = reg_wdata[8]; + + assign ie0_1_e_41_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_41_wd = reg_wdata[9]; + + assign ie0_1_e_42_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_42_wd = reg_wdata[10]; + + assign ie0_1_e_43_we = addr_hit[49] & reg_we & ~wr_err; + assign ie0_1_e_43_wd = reg_wdata[11]; + + + assign threshold0_we = addr_hit[50] & reg_we & ~wr_err; + assign threshold0_wd = reg_wdata[1:0]; + + assign cc0_we = addr_hit[51] & reg_we & ~wr_err; + assign cc0_wd = reg_wdata[7:0]; + assign cc0_re = addr_hit[51] && reg_re; + + assign msip0_we = addr_hit[52] & reg_we & ~wr_err; + assign msip0_wd = reg_wdata[0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = ip_0_p_0_qs; + reg_rdata_next[1] = ip_0_p_1_qs; + reg_rdata_next[2] = ip_0_p_2_qs; + reg_rdata_next[3] = ip_0_p_3_qs; + reg_rdata_next[4] = ip_0_p_4_qs; + reg_rdata_next[5] = ip_0_p_5_qs; + reg_rdata_next[6] = ip_0_p_6_qs; + reg_rdata_next[7] = ip_0_p_7_qs; + reg_rdata_next[8] = ip_0_p_8_qs; + reg_rdata_next[9] = ip_0_p_9_qs; + reg_rdata_next[10] = ip_0_p_10_qs; + reg_rdata_next[11] = ip_0_p_11_qs; + reg_rdata_next[12] = ip_0_p_12_qs; + reg_rdata_next[13] = ip_0_p_13_qs; + reg_rdata_next[14] = ip_0_p_14_qs; + reg_rdata_next[15] = ip_0_p_15_qs; + reg_rdata_next[16] = ip_0_p_16_qs; + reg_rdata_next[17] = ip_0_p_17_qs; + reg_rdata_next[18] = ip_0_p_18_qs; + reg_rdata_next[19] = ip_0_p_19_qs; + reg_rdata_next[20] = ip_0_p_20_qs; + reg_rdata_next[21] = ip_0_p_21_qs; + reg_rdata_next[22] = ip_0_p_22_qs; + reg_rdata_next[23] = ip_0_p_23_qs; + reg_rdata_next[24] = ip_0_p_24_qs; + reg_rdata_next[25] = ip_0_p_25_qs; + reg_rdata_next[26] = ip_0_p_26_qs; + reg_rdata_next[27] = ip_0_p_27_qs; + reg_rdata_next[28] = ip_0_p_28_qs; + reg_rdata_next[29] = ip_0_p_29_qs; + reg_rdata_next[30] = ip_0_p_30_qs; + reg_rdata_next[31] = ip_0_p_31_qs; + end + + addr_hit[1]: begin + reg_rdata_next[0] = ip_1_p_32_qs; + reg_rdata_next[1] = ip_1_p_33_qs; + reg_rdata_next[2] = ip_1_p_34_qs; + reg_rdata_next[3] = ip_1_p_35_qs; + reg_rdata_next[4] = ip_1_p_36_qs; + reg_rdata_next[5] = ip_1_p_37_qs; + reg_rdata_next[6] = ip_1_p_38_qs; + reg_rdata_next[7] = ip_1_p_39_qs; + reg_rdata_next[8] = ip_1_p_40_qs; + reg_rdata_next[9] = ip_1_p_41_qs; + reg_rdata_next[10] = ip_1_p_42_qs; + reg_rdata_next[11] = ip_1_p_43_qs; + end + addr_hit[2]: begin + reg_rdata_next[0] = le_0_le_0_qs; + reg_rdata_next[1] = le_0_le_1_qs; + reg_rdata_next[2] = le_0_le_2_qs; + reg_rdata_next[3] = le_0_le_3_qs; + reg_rdata_next[4] = le_0_le_4_qs; + reg_rdata_next[5] = le_0_le_5_qs; + reg_rdata_next[6] = le_0_le_6_qs; + reg_rdata_next[7] = le_0_le_7_qs; + reg_rdata_next[8] = le_0_le_8_qs; + reg_rdata_next[9] = le_0_le_9_qs; + reg_rdata_next[10] = le_0_le_10_qs; + reg_rdata_next[11] = le_0_le_11_qs; + reg_rdata_next[12] = le_0_le_12_qs; + reg_rdata_next[13] = le_0_le_13_qs; + reg_rdata_next[14] = le_0_le_14_qs; + reg_rdata_next[15] = le_0_le_15_qs; + reg_rdata_next[16] = le_0_le_16_qs; + reg_rdata_next[17] = le_0_le_17_qs; + reg_rdata_next[18] = le_0_le_18_qs; + reg_rdata_next[19] = le_0_le_19_qs; + reg_rdata_next[20] = le_0_le_20_qs; + reg_rdata_next[21] = le_0_le_21_qs; + reg_rdata_next[22] = le_0_le_22_qs; + reg_rdata_next[23] = le_0_le_23_qs; + reg_rdata_next[24] = le_0_le_24_qs; + reg_rdata_next[25] = le_0_le_25_qs; + reg_rdata_next[26] = le_0_le_26_qs; + reg_rdata_next[27] = le_0_le_27_qs; + reg_rdata_next[28] = le_0_le_28_qs; + reg_rdata_next[29] = le_0_le_29_qs; + reg_rdata_next[30] = le_0_le_30_qs; + reg_rdata_next[31] = le_0_le_31_qs; + end + + addr_hit[3]: begin + reg_rdata_next[0] = le_1_le_32_qs; + reg_rdata_next[1] = le_1_le_33_qs; + reg_rdata_next[2] = le_1_le_34_qs; + reg_rdata_next[3] = le_1_le_35_qs; + reg_rdata_next[4] = le_1_le_36_qs; + reg_rdata_next[5] = le_1_le_37_qs; + reg_rdata_next[6] = le_1_le_38_qs; + reg_rdata_next[7] = le_1_le_39_qs; + reg_rdata_next[8] = le_1_le_40_qs; + reg_rdata_next[9] = le_1_le_41_qs; + reg_rdata_next[10] = le_1_le_42_qs; + reg_rdata_next[11] = le_1_le_43_qs; + end + addr_hit[4]: begin + reg_rdata_next[1:0] = prio0_qs; + end + + addr_hit[5]: begin + reg_rdata_next[1:0] = prio1_qs; + end + + addr_hit[6]: begin + reg_rdata_next[1:0] = prio2_qs; + end + + addr_hit[7]: begin + reg_rdata_next[1:0] = prio3_qs; + end + + addr_hit[8]: begin + reg_rdata_next[1:0] = prio4_qs; + end + + addr_hit[9]: begin + reg_rdata_next[1:0] = prio5_qs; + end + + addr_hit[10]: begin + reg_rdata_next[1:0] = prio6_qs; + end + + addr_hit[11]: begin + reg_rdata_next[1:0] = prio7_qs; + end + + addr_hit[12]: begin + reg_rdata_next[1:0] = prio8_qs; + end + + addr_hit[13]: begin + reg_rdata_next[1:0] = prio9_qs; + end + + addr_hit[14]: begin + reg_rdata_next[1:0] = prio10_qs; + end + + addr_hit[15]: begin + reg_rdata_next[1:0] = prio11_qs; + end + + addr_hit[16]: begin + reg_rdata_next[1:0] = prio12_qs; + end + + addr_hit[17]: begin + reg_rdata_next[1:0] = prio13_qs; + end + + addr_hit[18]: begin + reg_rdata_next[1:0] = prio14_qs; + end + + addr_hit[19]: begin + reg_rdata_next[1:0] = prio15_qs; + end + + addr_hit[20]: begin + reg_rdata_next[1:0] = prio16_qs; + end + + addr_hit[21]: begin + reg_rdata_next[1:0] = prio17_qs; + end + + addr_hit[22]: begin + reg_rdata_next[1:0] = prio18_qs; + end + + addr_hit[23]: begin + reg_rdata_next[1:0] = prio19_qs; + end + + addr_hit[24]: begin + reg_rdata_next[1:0] = prio20_qs; + end + + addr_hit[25]: begin + reg_rdata_next[1:0] = prio21_qs; + end + + addr_hit[26]: begin + reg_rdata_next[1:0] = prio22_qs; + end + + addr_hit[27]: begin + reg_rdata_next[1:0] = prio23_qs; + end + + addr_hit[28]: begin + reg_rdata_next[1:0] = prio24_qs; + end + + addr_hit[29]: begin + reg_rdata_next[1:0] = prio25_qs; + end + + addr_hit[30]: begin + reg_rdata_next[1:0] = prio26_qs; + end + + addr_hit[31]: begin + reg_rdata_next[1:0] = prio27_qs; + end + + addr_hit[32]: begin + reg_rdata_next[1:0] = prio28_qs; + end + + addr_hit[33]: begin + reg_rdata_next[1:0] = prio29_qs; + end + + addr_hit[34]: begin + reg_rdata_next[1:0] = prio30_qs; + end + + addr_hit[35]: begin + reg_rdata_next[1:0] = prio31_qs; + end + + addr_hit[36]: begin + reg_rdata_next[1:0] = prio32_qs; + end + + addr_hit[37]: begin + reg_rdata_next[1:0] = prio33_qs; + end + + addr_hit[38]: begin + reg_rdata_next[1:0] = prio34_qs; + end + + addr_hit[39]: begin + reg_rdata_next[1:0] = prio35_qs; + end + + addr_hit[40]: begin + reg_rdata_next[1:0] = prio36_qs; + end + + addr_hit[41]: begin + reg_rdata_next[1:0] = prio37_qs; + end + + addr_hit[42]: begin + reg_rdata_next[1:0] = prio38_qs; + end + + addr_hit[43]: begin + reg_rdata_next[1:0] = prio39_qs; + end + + addr_hit[44]: begin + reg_rdata_next[1:0] = prio40_qs; + end + + addr_hit[45]: begin + reg_rdata_next[1:0] = prio41_qs; + end + + addr_hit[46]: begin + reg_rdata_next[1:0] = prio42_qs; + end + + addr_hit[47]: begin + reg_rdata_next[1:0] = prio43_qs; + end + + addr_hit[48]: begin + reg_rdata_next[0] = ie0_0_e_0_qs; + reg_rdata_next[1] = ie0_0_e_1_qs; + reg_rdata_next[2] = ie0_0_e_2_qs; + reg_rdata_next[3] = ie0_0_e_3_qs; + reg_rdata_next[4] = ie0_0_e_4_qs; + reg_rdata_next[5] = ie0_0_e_5_qs; + reg_rdata_next[6] = ie0_0_e_6_qs; + reg_rdata_next[7] = ie0_0_e_7_qs; + reg_rdata_next[8] = ie0_0_e_8_qs; + reg_rdata_next[9] = ie0_0_e_9_qs; + reg_rdata_next[10] = ie0_0_e_10_qs; + reg_rdata_next[11] = ie0_0_e_11_qs; + reg_rdata_next[12] = ie0_0_e_12_qs; + reg_rdata_next[13] = ie0_0_e_13_qs; + reg_rdata_next[14] = ie0_0_e_14_qs; + reg_rdata_next[15] = ie0_0_e_15_qs; + reg_rdata_next[16] = ie0_0_e_16_qs; + reg_rdata_next[17] = ie0_0_e_17_qs; + reg_rdata_next[18] = ie0_0_e_18_qs; + reg_rdata_next[19] = ie0_0_e_19_qs; + reg_rdata_next[20] = ie0_0_e_20_qs; + reg_rdata_next[21] = ie0_0_e_21_qs; + reg_rdata_next[22] = ie0_0_e_22_qs; + reg_rdata_next[23] = ie0_0_e_23_qs; + reg_rdata_next[24] = ie0_0_e_24_qs; + reg_rdata_next[25] = ie0_0_e_25_qs; + reg_rdata_next[26] = ie0_0_e_26_qs; + reg_rdata_next[27] = ie0_0_e_27_qs; + reg_rdata_next[28] = ie0_0_e_28_qs; + reg_rdata_next[29] = ie0_0_e_29_qs; + reg_rdata_next[30] = ie0_0_e_30_qs; + reg_rdata_next[31] = ie0_0_e_31_qs; + end + + addr_hit[49]: begin + reg_rdata_next[0] = ie0_1_e_32_qs; + reg_rdata_next[1] = ie0_1_e_33_qs; + reg_rdata_next[2] = ie0_1_e_34_qs; + reg_rdata_next[3] = ie0_1_e_35_qs; + reg_rdata_next[4] = ie0_1_e_36_qs; + reg_rdata_next[5] = ie0_1_e_37_qs; + reg_rdata_next[6] = ie0_1_e_38_qs; + reg_rdata_next[7] = ie0_1_e_39_qs; + reg_rdata_next[8] = ie0_1_e_40_qs; + reg_rdata_next[9] = ie0_1_e_41_qs; + reg_rdata_next[10] = ie0_1_e_42_qs; + reg_rdata_next[11] = ie0_1_e_43_qs; + end + + + addr_hit[50]: begin + reg_rdata_next[1:0] = threshold0_qs; + end + + addr_hit[51]: begin + reg_rdata_next[7:0] = cc0_qs; + end + + addr_hit[52]: begin + reg_rdata_next[0] = msip0_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + +endmodule
diff --git a/verilog/rtl/rv_plic_target.sv b/verilog/rtl/rv_plic_target.sv new file mode 100644 index 0000000..045c202 --- /dev/null +++ b/verilog/rtl/rv_plic_target.sv
@@ -0,0 +1,128 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// RISC-V Platform-Level Interrupt Generator for Target +// +// This module basically doing IE & IP based on priority and threshold_i. +// Keep in mind that increasing MAX_PRIO affects logic size a lot. +// +// The module implements a binary tree to find the maximal entry. the solution +// has O(N) area and O(log(N)) delay complexity, and thus scales well with +// many input sources. +// + + +module rv_plic_target #( + parameter int N_SOURCE = 32, + parameter int MAX_PRIO = 7, + + // Local param (Do not change this through parameter + localparam int SrcWidth = $clog2(N_SOURCE+1), // derived parameter + localparam int PrioWidth = $clog2(MAX_PRIO+1) // derived parameter +) ( + input clk_i, + input rst_ni, + + input [N_SOURCE-1:0] ip_i, + input [N_SOURCE-1:0] ie_i, + + input [PrioWidth-1:0] prio_i [N_SOURCE], + input [PrioWidth-1:0] threshold_i, + + output logic irq_o, + output logic [SrcWidth-1:0] irq_id_o +); + + // this only works with 2 or more sources + + + // align to powers of 2 for simplicity + // a full binary tree with N levels has 2**N + 2**N-1 nodes + localparam int NumLevels = $clog2(N_SOURCE); + logic [2**(NumLevels+1)-2:0] is_tree; + logic [2**(NumLevels+1)-2:0][SrcWidth-1:0] id_tree; + logic [2**(NumLevels+1)-2:0][PrioWidth-1:0] max_tree; + + for (genvar level = 0; level < NumLevels+1; level++) begin : gen_tree + // + // level+1 C0 C1 <- "Base1" points to the first node on "level+1", + // \ / these nodes are the children of the nodes one level below + // level Pa <- "Base0", points to the first node on "level", + // these nodes are the parents of the nodes one level above + // + // hence we have the following indices for the paPa, C0, C1 nodes: + // Pa = 2**level - 1 + offset = Base0 + offset + // C0 = 2**(level+1) - 1 + 2*offset = Base1 + 2*offset + // C1 = 2**(level+1) - 1 + 2*offset + 1 = Base1 + 2*offset + 1 + // + localparam int Base0 = (2**level)-1; + localparam int Base1 = (2**(level+1))-1; + + for (genvar offset = 0; offset < 2**level; offset++) begin : gen_level + localparam int Pa = Base0 + offset; + localparam int C0 = Base1 + 2*offset; + localparam int C1 = Base1 + 2*offset + 1; + + // this assigns the gated interrupt source signals, their + // corresponding IDs and priorities to the tree leafs + if (level == NumLevels) begin : gen_leafs + if (offset < N_SOURCE) begin : gen_assign + assign is_tree[Pa] = ip_i[offset] & ie_i[offset]; + assign id_tree[Pa] = offset; + assign max_tree[Pa] = prio_i[offset]; + end else begin : gen_tie_off + assign is_tree[Pa] = '0; + assign id_tree[Pa] = '0; + assign max_tree[Pa] = '0; + end + // this creates the node assignments + end else begin : gen_nodes + // NOTE: the code below has been written in this way in order to work + // around a synthesis issue in Vivado 2018.3 and 2019.2 where the whole + // module would be optimized away if these assign statements contained + // ternary statements to implement the muxes. + // + // TODO: rewrite these lines with ternary statmements onec the problem + // has been fixed in the tool. + // + // See also originating issue: + // https://github.com/lowRISC/opentitan/issues/1355 + // Xilinx issue: + // https://forums.xilinx.com/t5/Synthesis/Simulation-Synthesis-Mismatch-with-Vivado-2018-3/m-p/1065923#M33849 + + logic sel; // local helper variable + // in case only one of the parent has a pending irq_o, forward that one + // in case both irqs are pending, forward the one with higher priority + assign sel = (~is_tree[C0] & is_tree[C1]) | + (is_tree[C0] & is_tree[C1] & logic'(max_tree[C1] > max_tree[C0])); + // forwarding muxes + assign is_tree[Pa] = (sel & is_tree[C1]) | ((~sel) & is_tree[C0]); + assign id_tree[Pa] = ({SrcWidth{sel}} & id_tree[C1]) | ({SrcWidth{~sel}} & id_tree[C0]); + assign max_tree[Pa] = ({PrioWidth{sel}} & max_tree[C1]) | ({PrioWidth{~sel}} & max_tree[C0]); + end + end : gen_level + end : gen_tree + + logic irq_d, irq_q; + logic [SrcWidth-1:0] irq_id_d, irq_id_q; + + // the results can be found at the tree root + assign irq_d = (max_tree[0] > threshold_i) ? is_tree[0] : 1'b0; + assign irq_id_d = (is_tree[0]) ? id_tree[0] : '0; + + always_ff @(posedge clk_i or negedge rst_ni) begin : gen_regs + if (!rst_ni) begin + irq_q <= 1'b0; + irq_id_q <= '0; + end else begin + irq_q <= irq_d; + irq_id_q <= irq_id_d; + end + end + + assign irq_o = irq_q; + assign irq_id_o = irq_id_q; + +endmodule +
diff --git a/verilog/rtl/rv_timer.sv b/verilog/rtl/rv_timer.sv new file mode 100644 index 0000000..d88c28e --- /dev/null +++ b/verilog/rtl/rv_timer.sv
@@ -0,0 +1,129 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + + + + +module rv_timer ( + input clk_i, + input rst_ni, + + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + output logic intr_timer_expired_0_0_o +); + + localparam int N_HARTS = 1; + localparam int N_TIMERS = 1; + + import rv_timer_reg_pkg::*; + + rv_timer_reg2hw_t reg2hw; + rv_timer_hw2reg_t hw2reg; + + logic [N_HARTS-1:0] active; + + logic [11:0] prescaler [N_HARTS]; + logic [7:0] step [N_HARTS]; + + logic [N_HARTS-1:0] tick; + + logic [63:0] mtime_d [N_HARTS]; + logic [63:0] mtime [N_HARTS]; + logic [63:0] mtimecmp [N_HARTS][N_TIMERS]; // Only [harts][0] is connected to mtimecmp CSRs + logic mtimecmp_update [N_HARTS][N_TIMERS]; + + logic [N_HARTS*N_TIMERS-1:0] intr_timer_set; + logic [N_HARTS*N_TIMERS-1:0] intr_timer_en; + logic [N_HARTS*N_TIMERS-1:0] intr_timer_test_q; + logic [N_HARTS-1:0] intr_timer_test_qe; + logic [N_HARTS*N_TIMERS-1:0] intr_timer_state_q; + logic [N_HARTS-1:0] intr_timer_state_de; + logic [N_HARTS*N_TIMERS-1:0] intr_timer_state_d; + + logic [N_HARTS*N_TIMERS-1:0] intr_out; + + ///////////////////////////////////////////////// + // Connecting register interface to the signal // + ///////////////////////////////////////////////// + + // Once reggen supports nested multireg, the following can be automated. For the moment, it must + // be connected manually. + assign active[0] = reg2hw.ctrl[0].q; + assign prescaler = '{reg2hw.cfg0.prescale.q}; + assign step = '{reg2hw.cfg0.step.q}; + + assign hw2reg.timer_v_upper0.de = tick[0]; + assign hw2reg.timer_v_lower0.de = tick[0]; + assign hw2reg.timer_v_upper0.d = mtime_d[0][63:32]; + assign hw2reg.timer_v_lower0.d = mtime_d[0][31: 0]; + assign mtime[0] = {reg2hw.timer_v_upper0.q, reg2hw.timer_v_lower0.q}; + assign mtimecmp = '{'{{reg2hw.compare_upper0_0.q,reg2hw.compare_lower0_0.q}}}; + assign mtimecmp_update[0][0] = reg2hw.compare_upper0_0.qe | reg2hw.compare_lower0_0.qe; + + assign intr_timer_expired_0_0_o = intr_out[0]; + assign intr_timer_en = reg2hw.intr_enable0[0].q; + assign intr_timer_state_q = reg2hw.intr_state0[0].q; + assign intr_timer_test_q = reg2hw.intr_test0[0].q; + assign intr_timer_test_qe = reg2hw.intr_test0[0].qe; + assign hw2reg.intr_state0[0].de = intr_timer_state_de | mtimecmp_update[0][0]; + assign hw2reg.intr_state0[0].d = intr_timer_state_d & ~mtimecmp_update[0][0]; + + + for (genvar h = 0 ; h < N_HARTS ; h++) begin : gen_harts + prim_intr_hw #( + .Width(N_TIMERS) + ) u_intr_hw ( + .clk_i, + .rst_ni, + .event_intr_i (intr_timer_set), + + .reg2hw_intr_enable_q_i (intr_timer_en[h*N_TIMERS+:N_TIMERS]), + .reg2hw_intr_test_q_i (intr_timer_test_q[h*N_TIMERS+:N_TIMERS]), + .reg2hw_intr_test_qe_i (intr_timer_test_qe[h]), + .reg2hw_intr_state_q_i (intr_timer_state_q[h*N_TIMERS+:N_TIMERS]), + .hw2reg_intr_state_de_o (intr_timer_state_de), + .hw2reg_intr_state_d_o (intr_timer_state_d[h*N_TIMERS+:N_TIMERS]), + + .intr_o (intr_out[h*N_TIMERS+:N_TIMERS]) + ); + + timer_core #( + .N (N_TIMERS) + ) u_core ( + .clk_i, + .rst_ni, + + .active (active[h]), + .prescaler (prescaler[h]), + .step (step[h]), + + .tick (tick[h]), + + .mtime_d (mtime_d[h]), + .mtime (mtime[h]), + .mtimecmp (mtimecmp[h]), + + .intr (intr_timer_set[h*N_TIMERS+:N_TIMERS]) + ); + end : gen_harts + + // Register module + rv_timer_reg_top u_reg ( + .clk_i, + .rst_ni, + + .tl_i, + .tl_o, + + .reg2hw, + .hw2reg, + + .devmode_i (1'b1) + ); + + +endmodule
diff --git a/verilog/rtl/rv_timer_reg_pkg.sv b/verilog/rtl/rv_timer_reg_pkg.sv new file mode 100644 index 0000000..30cfb44 --- /dev/null +++ b/verilog/rtl/rv_timer_reg_pkg.sv
@@ -0,0 +1,142 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package rv_timer_reg_pkg; + + // Param list + parameter int N_HARTS = 1; + parameter int N_TIMERS = 1; + + // Address width within the block + parameter int BlockAw = 9; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + typedef struct packed { + logic q; + } rv_timer_reg2hw_ctrl_mreg_t; + + typedef struct packed { + struct packed { + logic [11:0] q; + } prescale; + struct packed { + logic [7:0] q; + } step; + } rv_timer_reg2hw_cfg0_reg_t; + + typedef struct packed { + logic [31:0] q; + } rv_timer_reg2hw_timer_v_lower0_reg_t; + + typedef struct packed { + logic [31:0] q; + } rv_timer_reg2hw_timer_v_upper0_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } rv_timer_reg2hw_compare_lower0_0_reg_t; + + typedef struct packed { + logic [31:0] q; + logic qe; + } rv_timer_reg2hw_compare_upper0_0_reg_t; + + typedef struct packed { + logic q; + } rv_timer_reg2hw_intr_enable0_mreg_t; + + typedef struct packed { + logic q; + } rv_timer_reg2hw_intr_state0_mreg_t; + + typedef struct packed { + logic q; + logic qe; + } rv_timer_reg2hw_intr_test0_mreg_t; + + + typedef struct packed { + logic [31:0] d; + logic de; + } rv_timer_hw2reg_timer_v_lower0_reg_t; + + typedef struct packed { + logic [31:0] d; + logic de; + } rv_timer_hw2reg_timer_v_upper0_reg_t; + + typedef struct packed { + logic d; + logic de; + } rv_timer_hw2reg_intr_state0_mreg_t; + + + /////////////////////////////////////// + // Register to internal design logic // + /////////////////////////////////////// + typedef struct packed { + rv_timer_reg2hw_ctrl_mreg_t [0:0] ctrl; // [154:154] + rv_timer_reg2hw_cfg0_reg_t cfg0; // [153:134] + rv_timer_reg2hw_timer_v_lower0_reg_t timer_v_lower0; // [133:102] + rv_timer_reg2hw_timer_v_upper0_reg_t timer_v_upper0; // [101:70] + rv_timer_reg2hw_compare_lower0_0_reg_t compare_lower0_0; // [69:37] + rv_timer_reg2hw_compare_upper0_0_reg_t compare_upper0_0; // [36:4] + rv_timer_reg2hw_intr_enable0_mreg_t [0:0] intr_enable0; // [3:3] + rv_timer_reg2hw_intr_state0_mreg_t [0:0] intr_state0; // [2:2] + rv_timer_reg2hw_intr_test0_mreg_t [0:0] intr_test0; // [1:0] + } rv_timer_reg2hw_t; + + /////////////////////////////////////// + // Internal design logic to register // + /////////////////////////////////////// + typedef struct packed { + rv_timer_hw2reg_timer_v_lower0_reg_t timer_v_lower0; // [67:35] + rv_timer_hw2reg_timer_v_upper0_reg_t timer_v_upper0; // [34:2] + rv_timer_hw2reg_intr_state0_mreg_t [0:0] intr_state0; // [1:0] + } rv_timer_hw2reg_t; + + // Register Address + parameter logic [BlockAw-1:0] RV_TIMER_CTRL_OFFSET = 9'h 0; + parameter logic [BlockAw-1:0] RV_TIMER_CFG0_OFFSET = 9'h 100; + parameter logic [BlockAw-1:0] RV_TIMER_TIMER_V_LOWER0_OFFSET = 9'h 104; + parameter logic [BlockAw-1:0] RV_TIMER_TIMER_V_UPPER0_OFFSET = 9'h 108; + parameter logic [BlockAw-1:0] RV_TIMER_COMPARE_LOWER0_0_OFFSET = 9'h 10c; + parameter logic [BlockAw-1:0] RV_TIMER_COMPARE_UPPER0_0_OFFSET = 9'h 110; + parameter logic [BlockAw-1:0] RV_TIMER_INTR_ENABLE0_OFFSET = 9'h 114; + parameter logic [BlockAw-1:0] RV_TIMER_INTR_STATE0_OFFSET = 9'h 118; + parameter logic [BlockAw-1:0] RV_TIMER_INTR_TEST0_OFFSET = 9'h 11c; + + + // Register Index + typedef enum int { + RV_TIMER_CTRL, + RV_TIMER_CFG0, + RV_TIMER_TIMER_V_LOWER0, + RV_TIMER_TIMER_V_UPPER0, + RV_TIMER_COMPARE_LOWER0_0, + RV_TIMER_COMPARE_UPPER0_0, + RV_TIMER_INTR_ENABLE0, + RV_TIMER_INTR_STATE0, + RV_TIMER_INTR_TEST0 + } rv_timer_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] RV_TIMER_PERMIT [9] = '{ + 4'b 0001, // index[0] RV_TIMER_CTRL + 4'b 0111, // index[1] RV_TIMER_CFG0 + 4'b 1111, // index[2] RV_TIMER_TIMER_V_LOWER0 + 4'b 1111, // index[3] RV_TIMER_TIMER_V_UPPER0 + 4'b 1111, // index[4] RV_TIMER_COMPARE_LOWER0_0 + 4'b 1111, // index[5] RV_TIMER_COMPARE_UPPER0_0 + 4'b 0001, // index[6] RV_TIMER_INTR_ENABLE0 + 4'b 0001, // index[7] RV_TIMER_INTR_STATE0 + 4'b 0001 // index[8] RV_TIMER_INTR_TEST0 + }; +endpackage +
diff --git a/verilog/rtl/rv_timer_reg_top.sv b/verilog/rtl/rv_timer_reg_top.sv new file mode 100644 index 0000000..62f1c51 --- /dev/null +++ b/verilog/rtl/rv_timer_reg_top.sv
@@ -0,0 +1,482 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + + +module rv_timer_reg_top ( + input clk_i, + input rst_ni, + + // Below Regster interface can be changed + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // To HW + output rv_timer_reg_pkg::rv_timer_reg2hw_t reg2hw, // Write + input rv_timer_reg_pkg::rv_timer_hw2reg_t hw2reg, // Read + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import rv_timer_reg_pkg::* ; + + localparam int AW = 9; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + tlul_pkg::tl_h2d_t tl_reg_h2d; + tlul_pkg::tl_d2h_t tl_reg_d2h; + + assign tl_reg_h2d = tl_i; + assign tl_o = tl_reg_d2h; + + tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW) + ) u_reg_if ( + .clk_i, + .rst_ni, + + .tl_i (tl_reg_h2d), + .tl_o (tl_reg_d2h), + + .we_o (reg_we), + .re_o (reg_re), + .addr_o (reg_addr), + .wdata_o (reg_wdata), + .be_o (reg_be), + .rdata_i (reg_rdata), + .error_i (reg_error) + ); + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err ; + + // Define SW related signals + // Format: <reg>_<field>_{wd|we|qs} + // or <reg>_{wd|we|qs} if field == 1 or 0 + logic ctrl_qs; + logic ctrl_wd; + logic ctrl_we; + logic [11:0] cfg0_prescale_qs; + logic [11:0] cfg0_prescale_wd; + logic cfg0_prescale_we; + logic [7:0] cfg0_step_qs; + logic [7:0] cfg0_step_wd; + logic cfg0_step_we; + logic [31:0] timer_v_lower0_qs; + logic [31:0] timer_v_lower0_wd; + logic timer_v_lower0_we; + logic [31:0] timer_v_upper0_qs; + logic [31:0] timer_v_upper0_wd; + logic timer_v_upper0_we; + logic [31:0] compare_lower0_0_qs; + logic [31:0] compare_lower0_0_wd; + logic compare_lower0_0_we; + logic [31:0] compare_upper0_0_qs; + logic [31:0] compare_upper0_0_wd; + logic compare_upper0_0_we; + logic intr_enable0_qs; + logic intr_enable0_wd; + logic intr_enable0_we; + logic intr_state0_qs; + logic intr_state0_wd; + logic intr_state0_we; + logic intr_test0_wd; + logic intr_test0_we; + + // Register instances + + // Subregister 0 of Multireg ctrl + // R[ctrl]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_we), + .wd (ctrl_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl[0].q ), + + // to register interface (read) + .qs (ctrl_qs) + ); + + + // R[cfg0]: V(False) + + // F[prescale]: 11:0 + prim_subreg #( + .DW (12), + .SWACCESS("RW"), + .RESVAL (12'h0) + ) u_cfg0_prescale ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (cfg0_prescale_we), + .wd (cfg0_prescale_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.cfg0.prescale.q ), + + // to register interface (read) + .qs (cfg0_prescale_qs) + ); + + + // F[step]: 23:16 + prim_subreg #( + .DW (8), + .SWACCESS("RW"), + .RESVAL (8'h1) + ) u_cfg0_step ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (cfg0_step_we), + .wd (cfg0_step_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.cfg0.step.q ), + + // to register interface (read) + .qs (cfg0_step_qs) + ); + + + // R[timer_v_lower0]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_timer_v_lower0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (timer_v_lower0_we), + .wd (timer_v_lower0_wd), + + // from internal hardware + .de (hw2reg.timer_v_lower0.de), + .d (hw2reg.timer_v_lower0.d ), + + // to internal hardware + .qe (), + .q (reg2hw.timer_v_lower0.q ), + + // to register interface (read) + .qs (timer_v_lower0_qs) + ); + + + // R[timer_v_upper0]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'h0) + ) u_timer_v_upper0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (timer_v_upper0_we), + .wd (timer_v_upper0_wd), + + // from internal hardware + .de (hw2reg.timer_v_upper0.de), + .d (hw2reg.timer_v_upper0.d ), + + // to internal hardware + .qe (), + .q (reg2hw.timer_v_upper0.q ), + + // to register interface (read) + .qs (timer_v_upper0_qs) + ); + + + // R[compare_lower0_0]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'hffffffff) + ) u_compare_lower0_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (compare_lower0_0_we), + .wd (compare_lower0_0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (reg2hw.compare_lower0_0.qe), + .q (reg2hw.compare_lower0_0.q ), + + // to register interface (read) + .qs (compare_lower0_0_qs) + ); + + + // R[compare_upper0_0]: V(False) + + prim_subreg #( + .DW (32), + .SWACCESS("RW"), + .RESVAL (32'hffffffff) + ) u_compare_upper0_0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (compare_upper0_0_we), + .wd (compare_upper0_0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (reg2hw.compare_upper0_0.qe), + .q (reg2hw.compare_upper0_0.q ), + + // to register interface (read) + .qs (compare_upper0_0_qs) + ); + + + + // Subregister 0 of Multireg intr_enable0 + // R[intr_enable0]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable0_we), + .wd (intr_enable0_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable0[0].q ), + + // to register interface (read) + .qs (intr_enable0_qs) + ); + + + + // Subregister 0 of Multireg intr_state0 + // R[intr_state0]: V(False) + + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state0 ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state0_we), + .wd (intr_state0_wd), + + // from internal hardware + .de (hw2reg.intr_state0[0].de), + .d (hw2reg.intr_state0[0].d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state0[0].q ), + + // to register interface (read) + .qs (intr_state0_qs) + ); + + + + // Subregister 0 of Multireg intr_test0 + // R[intr_test0]: V(True) + + prim_subreg_ext #( + .DW (1) + ) u_intr_test0 ( + .re (1'b0), + .we (intr_test0_we), + .wd (intr_test0_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test0[0].qe), + .q (reg2hw.intr_test0[0].q ), + .qs () + ); + + + + + logic [8:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == RV_TIMER_CTRL_OFFSET); + addr_hit[1] = (reg_addr == RV_TIMER_CFG0_OFFSET); + addr_hit[2] = (reg_addr == RV_TIMER_TIMER_V_LOWER0_OFFSET); + addr_hit[3] = (reg_addr == RV_TIMER_TIMER_V_UPPER0_OFFSET); + addr_hit[4] = (reg_addr == RV_TIMER_COMPARE_LOWER0_0_OFFSET); + addr_hit[5] = (reg_addr == RV_TIMER_COMPARE_UPPER0_0_OFFSET); + addr_hit[6] = (reg_addr == RV_TIMER_INTR_ENABLE0_OFFSET); + addr_hit[7] = (reg_addr == RV_TIMER_INTR_STATE0_OFFSET); + addr_hit[8] = (reg_addr == RV_TIMER_INTR_TEST0_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = 1'b0; + if (addr_hit[0] && reg_we && (RV_TIMER_PERMIT[0] != (RV_TIMER_PERMIT[0] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[1] && reg_we && (RV_TIMER_PERMIT[1] != (RV_TIMER_PERMIT[1] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[2] && reg_we && (RV_TIMER_PERMIT[2] != (RV_TIMER_PERMIT[2] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[3] && reg_we && (RV_TIMER_PERMIT[3] != (RV_TIMER_PERMIT[3] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[4] && reg_we && (RV_TIMER_PERMIT[4] != (RV_TIMER_PERMIT[4] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[5] && reg_we && (RV_TIMER_PERMIT[5] != (RV_TIMER_PERMIT[5] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[6] && reg_we && (RV_TIMER_PERMIT[6] != (RV_TIMER_PERMIT[6] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[7] && reg_we && (RV_TIMER_PERMIT[7] != (RV_TIMER_PERMIT[7] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[8] && reg_we && (RV_TIMER_PERMIT[8] != (RV_TIMER_PERMIT[8] & reg_be))) wr_err = 1'b1 ; + end + + assign ctrl_we = addr_hit[0] & reg_we & ~wr_err; + assign ctrl_wd = reg_wdata[0]; + + assign cfg0_prescale_we = addr_hit[1] & reg_we & ~wr_err; + assign cfg0_prescale_wd = reg_wdata[11:0]; + + assign cfg0_step_we = addr_hit[1] & reg_we & ~wr_err; + assign cfg0_step_wd = reg_wdata[23:16]; + + assign timer_v_lower0_we = addr_hit[2] & reg_we & ~wr_err; + assign timer_v_lower0_wd = reg_wdata[31:0]; + + assign timer_v_upper0_we = addr_hit[3] & reg_we & ~wr_err; + assign timer_v_upper0_wd = reg_wdata[31:0]; + + assign compare_lower0_0_we = addr_hit[4] & reg_we & ~wr_err; + assign compare_lower0_0_wd = reg_wdata[31:0]; + + assign compare_upper0_0_we = addr_hit[5] & reg_we & ~wr_err; + assign compare_upper0_0_wd = reg_wdata[31:0]; + + assign intr_enable0_we = addr_hit[6] & reg_we & ~wr_err; + assign intr_enable0_wd = reg_wdata[0]; + + assign intr_state0_we = addr_hit[7] & reg_we & ~wr_err; + assign intr_state0_wd = reg_wdata[0]; + + assign intr_test0_we = addr_hit[8] & reg_we & ~wr_err; + assign intr_test0_wd = reg_wdata[0]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = ctrl_qs; + end + + addr_hit[1]: begin + reg_rdata_next[11:0] = cfg0_prescale_qs; + reg_rdata_next[23:16] = cfg0_step_qs; + end + + addr_hit[2]: begin + reg_rdata_next[31:0] = timer_v_lower0_qs; + end + + addr_hit[3]: begin + reg_rdata_next[31:0] = timer_v_upper0_qs; + end + + addr_hit[4]: begin + reg_rdata_next[31:0] = compare_lower0_0_qs; + end + + addr_hit[5]: begin + reg_rdata_next[31:0] = compare_upper0_0_qs; + end + + addr_hit[6]: begin + reg_rdata_next[0] = intr_enable0_qs; + end + + addr_hit[7]: begin + reg_rdata_next[0] = intr_state0_qs; + end + + addr_hit[8]: begin + reg_rdata_next[0] = '0; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + +endmodule
diff --git a/verilog/rtl/spi_clgen.v b/verilog/rtl/spi_clgen.v new file mode 100644 index 0000000..2086eaa --- /dev/null +++ b/verilog/rtl/spi_clgen.v
@@ -0,0 +1,66 @@ + + +module spi_clgen ( + input clk_i, // input clock (system clock) + input rst_ni, // reset + input enable, // clock enable + input go, // start transfer + input last_clk, // last clock + input [`SPI_DIVIDER_LEN-1:0] divider, // clock divider (output clock is divided by this value) + output reg clk_out, // output clock + output reg pos_edge, // pulse marking positive edge of clk_out + output reg neg_edge // pulse marking negative edge of clk_out + +); + + //reg clk_out; + //reg pos_edge; + //reg neg_edge; + + reg [`SPI_DIVIDER_LEN-1:0] cnt; // clock counter + wire cnt_zero; // conter is equal to zero + wire cnt_one; // conter is equal to one + + + assign cnt_zero = cnt == {`SPI_DIVIDER_LEN{1'b0}}; + assign cnt_one = cnt == {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; + + // Counter counts half period + always @(posedge clk_i or negedge rst_ni) + begin + if(~rst_ni) + cnt <= {`SPI_DIVIDER_LEN{1'b1}}; + else + begin + if(!enable || cnt_zero) + cnt <= divider; + else + cnt <= cnt - {{`SPI_DIVIDER_LEN-1{1'b0}}, 1'b1}; + end + end + + // clk_out is asserted every other half period + always @(posedge clk_i or negedge rst_ni) + begin + if(~rst_ni) + clk_out <= 1'b0; + else + clk_out <= (enable && cnt_zero && (!last_clk || clk_out)) ? ~clk_out : clk_out; + end + + // Pos and neg edge signals + always @(posedge clk_i or negedge rst_ni) + begin + if(~rst_ni) + begin + pos_edge <= 1'b0; + neg_edge <= 1'b0; + end + else + begin + pos_edge <= (enable && !clk_out && cnt_one) || (!(|divider) && clk_out) || (!(|divider) && go && !enable); + neg_edge <= (enable && clk_out && cnt_one) || (!(|divider) && !clk_out && enable); + end + end +endmodule +
diff --git a/verilog/rtl/spi_core.sv b/verilog/rtl/spi_core.sv new file mode 100644 index 0000000..b1c2453 --- /dev/null +++ b/verilog/rtl/spi_core.sv
@@ -0,0 +1,199 @@ + +module spi_core +( + // tlul signals + input clk_i, + input rst_ni, + input [7:0] addr_i, + input [31:0] wdata_i, + output reg [31:0] rdata_o, + input [3:0] be_i, + input we_i, + input re_i, + output reg error_o, + output reg intr_rx_o, + output reg intr_tx_o, + + // SPI signals + output [`SPI_SS_NB-1:0] ss_o, // slave select + output sclk_o, // serial clock + output sd_o, + output reg sd_oe, // master out slave in + input sd_i // master in slave out +); + + + // Internal signals + reg [`SPI_DIVIDER_LEN-1:0] divider; // Divider register + reg [`SPI_CTRL_BIT_NB-1:0] ctrl; // Control and status register + reg [`SPI_SS_NB-1:0] ss; // Slave select register + reg [32-1:0] wb_dat; // wb data out + wire [`SPI_MAX_CHAR-1:0] rx; // Rx register + wire rx_negedge; // miso is sampled on negative edge + wire tx_negedge; // mosi is driven on negative edge + wire [`SPI_CHAR_LEN_BITS-1:0] char_len; // char len + wire go; // go + wire lsb; // lsb first on line + wire ie; // interrupt enable + wire ass; // automatic slave select + wire spi_divider_sel; // divider register select + wire spi_ctrl_sel; // ctrl register select + wire spi_tx_sel; // tx_l register select + wire spi_ss_sel; // ss register select + wire tip; // transfer in progress + wire pos_edge; // recognize posedge of sclk + wire neg_edge; // recognize negedge of sclk + wire last_bit; // marks last character bit + wire tx_en; // enables spi transmission + wire rx_en; // enables spi reception + + // Address decoder + assign spi_divider_sel = we_i & ~re_i & (addr_i[`SPI_OFS_BITS] == `SPI_DEVIDE); + assign spi_ctrl_sel = we_i & ~re_i & (addr_i[`SPI_OFS_BITS] == `SPI_CTRL); + assign spi_tx_sel = we_i & ~re_i & (addr_i[`SPI_OFS_BITS] == `SPI_TX_0) & tx_en; + assign spi_ss_sel = we_i & ~re_i & (addr_i[`SPI_OFS_BITS] == `SPI_SS); + + // Read from registers + always @(addr_i or rx or ctrl or divider or ss) + begin + case (addr_i[`SPI_OFS_BITS]) + `SPI_RX_0: wb_dat = rx[`SPI_MAX_CHAR-1:0]; + `SPI_CTRL: wb_dat = ctrl; + `SPI_DEVIDE: wb_dat = divider; + `SPI_SS: wb_dat = ss; + default: wb_dat = 32'b0; + endcase + end + + // Wb data out + always @(posedge clk_i) + begin + if (~rst_ni) + rdata_o <= 32'b0; + else + rdata_o <= wb_dat; + end + + + // Wb error + assign error_o = 1'b0; + + // Interrupt + always @(posedge clk_i) + begin + if (~rst_ni) + intr_tx_o <= 1'b0; + else if (ie && tip && last_bit && pos_edge && tx_en) + intr_tx_o <= 1'b1; + else + intr_tx_o <= 1'b0; + end + + always @(posedge clk_i ) + begin + if (~rst_ni) + intr_rx_o <= 1'b0; + else if (ie && tip && last_bit && pos_edge && rx_en) + intr_rx_o <= 1'b1; + else + intr_rx_o <= 1'b0; + end + + // Divider register + always @(posedge clk_i) + begin + if (~rst_ni) + divider <= {`SPI_DIVIDER_LEN{1'b0}}; + else if (spi_divider_sel && we_i && !tip) + begin + if (be_i[0]) + divider[7:0] <= wdata_i[7:0]; + if (be_i[1]) + divider[`SPI_DIVIDER_LEN-1:8] <= wdata_i[`SPI_DIVIDER_LEN-1:8]; + end + end + + // Ctrl register + always @(posedge clk_i) + begin + if (~rst_ni) + ctrl <= {`SPI_CTRL_BIT_NB{1'b0}}; + else if(spi_ctrl_sel && we_i && !tip) + begin + if (be_i[0]) + ctrl[7:0] <= wdata_i[7:0] | {7'b0, ctrl[0]}; + if (be_i[1]) + ctrl[`SPI_CTRL_BIT_NB-1:8] <= wdata_i[`SPI_CTRL_BIT_NB-1:8]; + end + else if(tip && last_bit && pos_edge) + ctrl[`SPI_CTRL_GO] <= 1'b0; + end + + assign rx_negedge = ctrl[`SPI_CTRL_RX_NEGEDGE]; + assign tx_negedge = ctrl[`SPI_CTRL_TX_NEGEDGE]; + assign go = ctrl[`SPI_CTRL_GO]; + assign char_len = ctrl[`SPI_CTRL_CHAR_LEN]; + assign lsb = ctrl[`SPI_CTRL_LSB]; + assign ie = ctrl[`SPI_CTRL_IE]; + assign ass = ctrl[`SPI_CTRL_ASS]; + assign rx_en = ctrl[`SPI_RX_SEL]; + assign tx_en = ctrl[`SPI_TX_SEL]; + + always @(posedge clk_i or negedge rst_ni) begin + if(~rst_ni) begin + sd_oe <= 1'b0; + end else if (tx_en & !rx_en) begin + sd_oe <= 1'b1; + end else begin + sd_oe <= 1'b0; + end + end + // Slave select register + always @(posedge clk_i) + begin + if (~rst_ni) + ss <= {`SPI_SS_NB{1'b0}}; + else if(spi_ss_sel && we_i && !tip) + begin + if (be_i[0]) + ss <= wdata_i[`SPI_SS_NB-1:0]; + end + end + + assign ss_o = ~((ss & {`SPI_SS_NB{tip & ass}}) | (ss & {`SPI_SS_NB{!ass}})); + + spi_clgen clgen ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .go (go), + .enable (tip), + .last_clk (last_bit), + .divider (divider), + .clk_out (sclk_o), + .pos_edge (pos_edge), + .neg_edge (neg_edge) + ); + + spi_shift shift ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .len (char_len[`SPI_CHAR_LEN_BITS-1:0]), + .latch (spi_tx_sel & we_i), + .byte_sel (be_i), + .lsb (lsb), + .go (go), + .pos_edge (pos_edge), + .neg_edge (neg_edge), + .rx_negedge (rx_negedge), + .tx_negedge (tx_negedge), + .tip (tip), + .last (last_bit), + .p_in (wdata_i), + .p_out (rx), + .s_clk (sclk_o), + .s_in (sd_i), + .s_out (sd_o), + .rx_en (rx_en) + ); +endmodule +
diff --git a/verilog/rtl/spi_defines.v b/verilog/rtl/spi_defines.v new file mode 100644 index 0000000..3e43ac9 --- /dev/null +++ b/verilog/rtl/spi_defines.v
@@ -0,0 +1,117 @@ + +// Number of bits used for devider register. If used in system with +// low frequency of system clock this can be reduced. +// Use SPI_DIVIDER_LEN for fine tuning theexact number. +// +//`define SPI_DIVIDER_LEN_8 +`define SPI_DIVIDER_LEN_16 +//`define SPI_DIVIDER_LEN_24 +//`define SPI_DIVIDER_LEN_32 + +`ifdef SPI_DIVIDER_LEN_8 + `define SPI_DIVIDER_LEN 8 // Can be set from 1 to 8 +`endif +`ifdef SPI_DIVIDER_LEN_16 + `define SPI_DIVIDER_LEN 16 // Can be set from 9 to 16 +`endif +`ifdef SPI_DIVIDER_LEN_24 + `define SPI_DIVIDER_LEN 24 // Can be set from 17 to 24 +`endif +`ifdef SPI_DIVIDER_LEN_32 + `define SPI_DIVIDER_LEN 32 // Can be set from 25 to 32 +`endif + +// +// Maximum nuber of bits that can be send/received at once. +// Use SPI_MAX_CHAR for fine tuning the exact number, when using +// SPI_MAX_CHAR_32, SPI_MAX_CHAR_24, SPI_MAX_CHAR_16, SPI_MAX_CHAR_8. +// +`define SPI_MAX_CHAR_32 +//`define SPI_MAX_CHAR_64 +//`define SPI_MAX_CHAR_32 +//`define SPI_MAX_CHAR_24 +//`define SPI_MAX_CHAR_16 +//`define SPI_MAX_CHAR_8 + +`ifdef SPI_MAX_CHAR_128 + `define SPI_MAX_CHAR 128 // Can only be set to 128 + `define SPI_CHAR_LEN_BITS 7 +`endif +`ifdef SPI_MAX_CHAR_64 + `define SPI_MAX_CHAR 64 // Can only be set to 64 + `define SPI_CHAR_LEN_BITS 6 +`endif +`ifdef SPI_MAX_CHAR_32 + `define SPI_MAX_CHAR 32 // Can be set from 25 to 32 + `define SPI_CHAR_LEN_BITS 5 +`endif +`ifdef SPI_MAX_CHAR_24 + `define SPI_MAX_CHAR 24 // Can be set from 17 to 24 + `define SPI_CHAR_LEN_BITS 5 +`endif +`ifdef SPI_MAX_CHAR_16 + `define SPI_MAX_CHAR 16 // Can be set from 9 to 16 + `define SPI_CHAR_LEN_BITS 4 +`endif +`ifdef SPI_MAX_CHAR_8 + `define SPI_MAX_CHAR 8 // Can be set from 1 to 8 + `define SPI_CHAR_LEN_BITS 3 +`endif + +// +// Number of device select signals. Use SPI_SS_NB for fine tuning the +// exact number. +// +`define SPI_SS_NB_4 +//`define SPI_SS_NB_16 +//`define SPI_SS_NB_24 +//`define SPI_SS_NB_32 +`ifdef SPI_SS_NB_4 + `define SPI_SS_NB 4 // Can be set from 1 to 4 +`endif +`ifdef SPI_SS_NB_8 + `define SPI_SS_NB 8 // Can be set from 1 to 8 +`endif +`ifdef SPI_SS_NB_16 + `define SPI_SS_NB 16 // Can be set from 9 to 16 +`endif +`ifdef SPI_SS_NB_24 + `define SPI_SS_NB 24 // Can be set from 17 to 24 +`endif +`ifdef SPI_SS_NB_32 + `define SPI_SS_NB 32 // Can be set from 25 to 32 +`endif + +// +// Bits of WISHBONE address used for partial decoding of SPI registers. +// +`define SPI_OFS_BITS 6:2 + +// +// Register offset +// +`define SPI_RX_0 8 +`define SPI_TX_0 0 +`define SPI_CTRL 4 +`define SPI_DEVIDE 5 +`define SPI_SS 6 + +// +// Number of bits in ctrl register +// +`define SPI_CTRL_BIT_NB 16 + +// +// Control register bit position +// +`define SPI_RX_SEL 15 +`define SPI_TX_SEL 14 +`define SPI_CTRL_ASS 13 +`define SPI_CTRL_IE 12 +`define SPI_CTRL_LSB 11 +`define SPI_CTRL_TX_NEGEDGE 10 +`define SPI_CTRL_RX_NEGEDGE 9 +`define SPI_CTRL_GO 8 +`define SPI_CTRL_RES_1 7 +`define SPI_CTRL_CHAR_LEN 6:0 +
diff --git a/verilog/rtl/spi_shift.v b/verilog/rtl/spi_shift.v new file mode 100644 index 0000000..9bc6067 --- /dev/null +++ b/verilog/rtl/spi_shift.v
@@ -0,0 +1,103 @@ + + +module spi_shift ( + input clk_i, // system clock + input rst_ni, // reset + input latch, // latch signal for storing the data in shift register + input [3:0] byte_sel, // byte select signals for storing the data in shift register + input [`SPI_CHAR_LEN_BITS-1:0] len, // data len in bits (minus one) + input lsb, // lbs first_ni on the line + input go, // start stansfer + input pos_edge, // recognize posedge of sclk_i + input neg_edge, // recognize negedge of sclk_i + input rx_negedge, // s_in is sampled on negative edge + input tx_negedge, // s_out is driven on negative edge + output reg tip, // transfer in progress + output last, // last bit + input [31:0] p_in, // parallel in + output [`SPI_MAX_CHAR-1:0] p_out, // parallel out + input s_clk, // serial clock + input s_in, // serial in + output reg s_out, // serial out + input rx_en // serial rx enable +); + + // reg s_out; + // reg tip; + + reg [`SPI_CHAR_LEN_BITS:0] cnt; // data bit count + reg [`SPI_MAX_CHAR-1:0] data; + reg [`SPI_MAX_CHAR-1:0] data_rx; // shift register + wire [`SPI_CHAR_LEN_BITS:0] tx_bit_pos; // next bit position + wire [`SPI_CHAR_LEN_BITS:0] rx_bit_pos; // next bit position + wire rx_clk_i; // rx clock enable + wire tx_clk_i; // tx clock enable + + assign p_out = data_rx; + + assign tx_bit_pos = lsb ? {!(|len), len} - cnt : cnt - {{`SPI_CHAR_LEN_BITS{1'b0}},1'b1}; + assign rx_bit_pos = lsb ? {!(|len), len} - (rx_negedge ? cnt + {{`SPI_CHAR_LEN_BITS{1'b0}},1'b1} : cnt) : + (rx_negedge ? cnt : cnt - {{`SPI_CHAR_LEN_BITS{1'b0}},1'b1}); + + assign last = !(|cnt); + + assign rx_clk_i = (rx_negedge ? neg_edge : pos_edge) && (!last || s_clk); + assign tx_clk_i = (tx_negedge ? neg_edge : pos_edge) && !last; + + // Character bit counter + always @(posedge clk_i or negedge rst_ni) + begin + if(~rst_ni) + cnt <= {`SPI_CHAR_LEN_BITS+1{1'b0}}; + else + begin + if(tip) + cnt <= pos_edge ? (cnt - {{`SPI_CHAR_LEN_BITS{1'b0}}, 1'b1}) : cnt; + else + cnt <= !(|len) ? {1'b1, {`SPI_CHAR_LEN_BITS{1'b0}}} : {1'b0, len}; + end + end + + // Transfer in progress + always @(posedge clk_i or negedge rst_ni) + begin + if(~rst_ni) + tip <= 1'b0; + else if(go && ~tip) + tip <= 1'b1; + else if(tip && last && pos_edge) + tip <= 1'b0; + end + + // Sending bits to the line + always @(posedge clk_i or negedge rst_ni) + begin + if (~rst_ni) + s_out <= 1'b0; + else + s_out <= (tx_clk_i || !tip) ? data[tx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] : s_out; + end + + // Receiving bits from the line + always @(posedge clk_i ) + begin + if (~rst_ni) + data <= {`SPI_MAX_CHAR{1'b0}}; + else if (latch && !tip) + begin + if (byte_sel[0]) + data[7:0] <= p_in[7:0]; + if (byte_sel[1]) + data[15:8] <= p_in[15:8]; + if (byte_sel[2]) + data[23:16] <= p_in[23:16]; + if (byte_sel[3]) + data[`SPI_MAX_CHAR-1:24] <= p_in[`SPI_MAX_CHAR-1:24]; + end + else if (rx_en && tip) begin + data_rx[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]] <= rx_clk_i ? s_in : data_rx[rx_bit_pos[`SPI_CHAR_LEN_BITS-1:0]]; + end + end + +endmodule +
diff --git a/verilog/rtl/spi_top.sv b/verilog/rtl/spi_top.sv new file mode 100644 index 0000000..6c65601 --- /dev/null +++ b/verilog/rtl/spi_top.sv
@@ -0,0 +1,75 @@ + +module spi_top( + + input clk_i, + input rst_ni, + + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + // SPI signals + output intr_rx_o, + output intr_tx_o, + output [`SPI_SS_NB-1:0] ss_o, + output sclk_o, + output sd_o, + output sd_oe, + input sd_i + +); + +localparam int AW = 8; +localparam int DW = 32; + +logic re; +logic we; +logic [7:0] addr; +logic [31:0] wdata; +logic [3:0] be; +logic [31:0] rdata; +logic err; + +spi_core spi_host( + // tlul signals + .clk_i, + .rst_ni, + .addr_i (addr), + .wdata_i (wdata), + .rdata_o (rdata), + .be_i (be), + .we_i (we), + .re_i (re), + .error_o (err), + + .intr_rx_o (intr_rx_o), + .intr_tx_o (intr_tx_o), + + // SPI signals + .ss_o (ss_o), // slave select + .sclk_o (sclk_o), // serial clock + .sd_o (sd_o), // master out slave in + .sd_oe (sd_oe), + .sd_i (sd_i) // master in slave out +); + + +tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW) +) u_reg_if ( + .clk_i, + .rst_ni, + + .tl_i (tl_i), + .tl_o (tl_o), + + .we_o (we), + .re_o (re), + .addr_o (addr), + .wdata_o (wdata), + .be_o (be), + .rdata_i (rdata), + .error_i (err) +); + +endmodule
diff --git a/verilog/rtl/sram.v b/verilog/rtl/sram.v new file mode 100644 index 0000000..8fac671 --- /dev/null +++ b/verilog/rtl/sram.v
@@ -0,0 +1,126 @@ +// OpenRAM SRAM model +// Words: 1024 +// Word size: 32 +// Write size: 8 + +module sram #( + parameter NUM_WMASKS = 4 , + parameter DATA_WIDTH = 32 , + parameter ADDR_WIDTH = 10 , + parameter RAM_DEPTH = 1 << ADDR_WIDTH, + // FIXME: This delay is arbitrary. + parameter VERBOSE = 1 , //Set to 0 to only display warnings + parameter T_HOLD = 1 ,//Delay to hold dout value after posedge. Value is arbitrary + parameter IZERO = 0 , // binary / Initial RAM with zeros (has priority over INITFILE) + parameter IFILE = "" +) +( +// Port 0: RW + clk0,csb0,web0,wmask0,addr0,din0,dout0, +// Port 1: R + clk1,csb1,addr1,dout1 + ); + + /*parameter NUM_WMASKS = 4 ; + parameter DATA_WIDTH = 32 ; + parameter ADDR_WIDTH = 10 ; + parameter RAM_DEPTH = 1 << ADDR_WIDTH; + // FIXME: This delay is arbitrary. + parameter DELAY = 3 ; + parameter VERBOSE = 1 ; //Set to 0 to only display warnings + parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary + parameter IZERO = 0 ; // binary / Initial RAM with zeros (has priority over INITFILE) + parameter IFILE = ""; +*/ + input clk0; // clock + input csb0; // active low chip select + input web0; // active low write control + input [NUM_WMASKS-1:0] wmask0; // write mask + input [ADDR_WIDTH-1:0] addr0; + input [DATA_WIDTH-1:0] din0; + output [DATA_WIDTH-1:0] dout0; + input clk1; // clock + input csb1; // active low chip select + input [ADDR_WIDTH-1:0] addr1; + output [DATA_WIDTH-1:0] dout1; + + reg csb0_reg; + reg web0_reg; + reg [NUM_WMASKS-1:0] wmask0_reg; + reg [ADDR_WIDTH-1:0] addr0_reg; + reg [DATA_WIDTH-1:0] din0_reg; + reg [DATA_WIDTH-1:0] dout0; + + // All inputs are registers + always @(posedge clk0) + begin + csb0_reg = csb0; + web0_reg = web0; + wmask0_reg = wmask0; + addr0_reg = addr0; + din0_reg = din0; + //dout0 = 32'bx; + //if ( !csb0_reg && web0_reg && VERBOSE ) + //$display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]); + //if ( !csb0_reg && !web0_reg && VERBOSE ) + //$display($time," Writing %m addr0=%b din0=%b wmask0=%b",addr0_reg,din0_reg,wmask0_reg); + end + + reg csb1_reg; + reg [ADDR_WIDTH-1:0] addr1_reg; + reg [DATA_WIDTH-1:0] dout1; + + + // All inputs are registers + always @(posedge clk1) + begin + csb1_reg = csb1; + addr1_reg = addr1; + //if (!csb0 && !web0 && !csb1 && (addr0 == addr1)) + //$display($time," WARNING: Writing and reading addr0=%b and addr1=%b simultaneously!",addr0,addr1); + //#(T_HOLD) dout1 = 32'bx; + //if ( !csb1_reg && VERBOSE ) + //$display($time," Reading %m addr1=%b dout1=%b",addr1_reg,mem[addr1_reg]); + end + +integer i; +reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1]; +//initial + //if (IZERO) + //for (i=0; i<RAM_DEPTH; i=i+1) mem[i] = {DATA_WIDTH{1'b0}}; + //else + //if (IFILE != "") $readmemh({IFILE,".hex"}, mem); + + // Memory Write Block Port 0 + // Write Operation : When web0 = 0, csb0 = 0 + always @ (negedge clk0) + begin : MEM_WRITE0 + if ( !csb0_reg && !web0_reg ) begin + if (wmask0_reg[0]) + mem[addr0_reg][7:0] = din0_reg[7:0]; + if (wmask0_reg[1]) + mem[addr0_reg][15:8] = din0_reg[15:8]; + if (wmask0_reg[2]) + mem[addr0_reg][23:16] = din0_reg[23:16]; + if (wmask0_reg[3]) + mem[addr0_reg][31:24] = din0_reg[31:24]; + end + end + + // Memory Read Block Port 0 + // Read Operation : When web0 = 1, csb0 = 0 + always @ (negedge clk0) + begin : MEM_READ0 + if (!csb0_reg && web0_reg) + dout0 <= mem[addr0_reg]; + end + + // Memory Read Block Port 1 + // Read Operation : When web1 = 1, csb1 = 0 + always @ (negedge clk1) + begin : MEM_READ1 + if (!csb1_reg) + dout1 <= mem[addr1_reg]; + end + +endmodule
diff --git a/verilog/rtl/timer_core.sv b/verilog/rtl/timer_core.sv new file mode 100644 index 0000000..7cd1c62 --- /dev/null +++ b/verilog/rtl/timer_core.sv
@@ -0,0 +1,49 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// + + +module timer_core #( + parameter int N = 1 +) ( + input clk_i, + input rst_ni, + + input active, + input [11:0] prescaler, + input [ 7:0] step, + + output logic tick, + output logic [63:0] mtime_d, + input [63:0] mtime, + input [63:0] mtimecmp [N], + + output logic [N-1:0] intr +); + + logic [11:0] tick_count; + + always_ff @(posedge clk_i or negedge rst_ni) begin : generate_tick + if (!rst_ni) begin + tick_count <= 12'h0; + end else if (!active) begin + tick_count <= 12'h0; + end else if (tick_count == prescaler) begin + tick_count <= 12'h0; + end else begin + tick_count <= tick_count + 1'b1; + end + end + + assign tick = active & (tick_count >= prescaler); + + assign mtime_d = mtime + 64'(step); + + // interrupt is generated if mtime is greater than or equal to mtimecmp + // TODO: Check if it must consider overflow case + for (genvar t = 0 ; t < N ; t++) begin : gen_intr + assign intr[t] = active & (mtime >= mtimecmp[t]); + end + +endmodule : timer_core
diff --git a/verilog/rtl/tl_main_pkg.sv b/verilog/rtl/tl_main_pkg.sv new file mode 100644 index 0000000..4421478 --- /dev/null +++ b/verilog/rtl/tl_main_pkg.sv
@@ -0,0 +1,50 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// tl_main package generated by `tlgen.py` tool + +package tl_main_pkg; + + localparam logic [31:0] ADDR_SPACE_ICCM = 32'h 20000000; + localparam logic [31:0] ADDR_SPACE_DEBUG_ROM = 32'h 10040000; + localparam logic [31:0] ADDR_SPACE_DCCM = 32'h 10000000; + localparam logic [31:0] ADDR_SPACE_TIMER0 = 32'h 40000000; + localparam logic [31:0] ADDR_SPACE_UART0 = 32'h 40060000; + localparam logic [31:0] ADDR_SPACE_SPI0 = 32'h 40080000; + localparam logic [31:0] ADDR_SPACE_PWM = 32'h 400b0000; + localparam logic [31:0] ADDR_SPACE_GPIO = 32'h 400c0000; + localparam logic [31:0] ADDR_SPACE_PLIC = 32'h 40050000; + + localparam logic [31:0] ADDR_MASK_ICCM = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_DEBUG_ROM = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_DCCM = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_TIMER0 = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_UART0 = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_SPI0 = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_PWM = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_GPIO = 32'h 0000ffff; + localparam logic [31:0] ADDR_MASK_PLIC = 32'h 0000ffff; + + localparam int N_HOST = 3; + localparam int N_DEVICE = 9; + + typedef enum int { + TlIccm = 0, + TlDebugRom = 1, + TlDccm = 2, + TlTimer0 = 3, + TlUart0 = 4, + TlSpi0 = 5, + TlPwm = 6, + TlGpio = 7, + TlPlic = 8 + } tl_device_e; + + typedef enum int { + TlBrqif = 0, + TlBrqlsu = 1, + TlDmSba = 2 + } tl_host_e; + +endpackage
diff --git a/verilog/rtl/tl_xbar_main.sv b/verilog/rtl/tl_xbar_main.sv new file mode 100644 index 0000000..65f3a0b --- /dev/null +++ b/verilog/rtl/tl_xbar_main.sv
@@ -0,0 +1,424 @@ + +// main XBAR + +module tl_xbar_main ( + + input clk_i, + input rst_ni, + + + // Host interfaces + input tlul_pkg::tl_h2d_t tl_brqif_i, + output tlul_pkg::tl_d2h_t tl_brqif_o, + input tlul_pkg::tl_h2d_t tl_brqlsu_i, + output tlul_pkg::tl_d2h_t tl_brqlsu_o, + input tlul_pkg::tl_h2d_t tl_dm_sba_i, + output tlul_pkg::tl_d2h_t tl_dm_sba_o, + + // Device interfaces + output tlul_pkg::tl_h2d_t tl_iccm_o, + input tlul_pkg::tl_d2h_t tl_iccm_i, + output tlul_pkg::tl_h2d_t tl_debug_rom_o, + input tlul_pkg::tl_d2h_t tl_debug_rom_i, + output tlul_pkg::tl_h2d_t tl_dccm_o, + input tlul_pkg::tl_d2h_t tl_dccm_i, + output tlul_pkg::tl_h2d_t tl_timer0_o, + input tlul_pkg::tl_d2h_t tl_timer0_i, + output tlul_pkg::tl_h2d_t tl_uart_o, + input tlul_pkg::tl_d2h_t tl_uart_i, + output tlul_pkg::tl_h2d_t tl_spi_o, + input tlul_pkg::tl_d2h_t tl_spi_i, + output tlul_pkg::tl_h2d_t tl_pwm_o, + input tlul_pkg::tl_d2h_t tl_pwm_i, + output tlul_pkg::tl_h2d_t tl_gpio_o, + input tlul_pkg::tl_d2h_t tl_gpio_i, + output tlul_pkg::tl_h2d_t tl_plic_o, + input tlul_pkg::tl_d2h_t tl_plic_i + + +); + + import tlul_pkg::*; + import tl_main_pkg::*; + + // scanmode_i is currently not used, but provisioned for future use + // this assignment prevents lint warnings + + +// host 1 IFU + tlul_pkg::tl_h2d_t brqifu_to_s1n; + tlul_pkg::tl_d2h_t s1n_to_brqifu; + logic [1:0] device_sel_1; + +// host 2 LSU + tlul_pkg::tl_h2d_t brqlsu_to_s1n; + tlul_pkg::tl_d2h_t s1n_to_brqlsu; + logic [3:0] device_sel_2; + +// host 3 debug system bus access + tlul_pkg::tl_h2d_t dbg_to_s1n; + tlul_pkg::tl_d2h_t s1n_to_dbg; + logic [3:0] device_sel_3; + +// Dveice connections + + tlul_pkg::tl_h2d_t h1_dv_i[2]; + tlul_pkg::tl_d2h_t h1_dv_o[2]; + tlul_pkg::tl_h2d_t h2_dv_i[9]; + tlul_pkg::tl_d2h_t h2_dv_o[9]; + tlul_pkg::tl_h2d_t h3_dv_i[8]; + tlul_pkg::tl_d2h_t h3_dv_o[8]; + +// ICCM + tlul_pkg::tl_h2d_t s1n_sm1_1[3]; + tlul_pkg::tl_d2h_t sm1_s1n_1[3]; + +// DCCM + tlul_pkg::tl_h2d_t s1n_sm1_2[2]; + tlul_pkg::tl_d2h_t sm1_s1n_2[2]; + +// DEBUG ROM + tlul_pkg::tl_h2d_t s1n_sm1_4[2]; + tlul_pkg::tl_d2h_t sm1_s1n_4[2]; + +// TIMER + tlul_pkg::tl_h2d_t s1n_sm1_5[2]; + tlul_pkg::tl_d2h_t sm1_s1n_5[2]; + +// UART + tlul_pkg::tl_h2d_t s1n_sm1_6[2]; + tlul_pkg::tl_d2h_t sm1_s1n_6[2]; + +// SPI + tlul_pkg::tl_h2d_t s1n_sm1_7[2]; + tlul_pkg::tl_d2h_t sm1_s1n_7[2]; + +// PWM + tlul_pkg::tl_h2d_t s1n_sm1_8[2]; + tlul_pkg::tl_d2h_t sm1_s1n_8[2]; + +// GPIO + tlul_pkg::tl_h2d_t s1n_sm1_9[2]; + tlul_pkg::tl_d2h_t sm1_s1n_9[2]; + +// PLIC + tlul_pkg::tl_h2d_t s1n_sm1_10[2]; + tlul_pkg::tl_d2h_t sm1_s1n_10[2]; + +// Device 1 host connections (ICCM) + assign h1_dv_o[0] = sm1_s1n_1[0]; + assign h3_dv_o[1] = sm1_s1n_1[1]; + assign h2_dv_o[8] = sm1_s1n_1[2]; + assign s1n_sm1_1[0] = h1_dv_i[0]; + assign s1n_sm1_1[1] = h3_dv_i[1]; + assign s1n_sm1_1[2] = h2_dv_i[8]; + +// Device 2 host connections (DCCM) + assign h2_dv_o[0] = sm1_s1n_2[0]; + assign h3_dv_o[0] = sm1_s1n_2[1]; + assign s1n_sm1_2[0] = h2_dv_i[0]; + assign s1n_sm1_2[1] = h3_dv_i[0]; + +// Device 3 host connections (DEBUG ROM) + assign h1_dv_o[1] = sm1_s1n_4[0]; + assign h2_dv_o[1] = sm1_s1n_4[1]; + assign s1n_sm1_4[0] = h1_dv_i[1]; + assign s1n_sm1_4[1] = h2_dv_i[1]; + +// Device 4 host connections (TIMER0) + assign h2_dv_o[2] = sm1_s1n_5[0]; + assign h3_dv_o[2] = sm1_s1n_5[1]; + assign s1n_sm1_5[0] = h2_dv_i[2]; + assign s1n_sm1_5[1] = h3_dv_i[2]; + +// Device 5 host connections (UART) + assign h2_dv_o[3] = sm1_s1n_6[0]; + assign h3_dv_o[3] = sm1_s1n_6[1]; + assign s1n_sm1_6[0] = h2_dv_i[3]; + assign s1n_sm1_6[1] = h3_dv_i[3]; + +// Device 6 host connections (SPI) + assign h2_dv_o[4] = sm1_s1n_7[0]; + assign h3_dv_o[4] = sm1_s1n_7[1]; + assign s1n_sm1_7[0] = h2_dv_i[4]; + assign s1n_sm1_7[1] = h3_dv_i[4]; + +// Device 7 host connections (PWM) + assign h2_dv_o[5] = sm1_s1n_8[0]; + assign h3_dv_o[5] = sm1_s1n_8[1]; + assign s1n_sm1_8[0] = h2_dv_i[5]; + assign s1n_sm1_8[1] = h3_dv_i[5]; + +// Device 8 host connections (GPIO) + assign h2_dv_o[6] = sm1_s1n_9[0]; + assign h3_dv_o[6] = sm1_s1n_9[1]; + assign s1n_sm1_9[0] = h2_dv_i[6]; + assign s1n_sm1_9[1] = h3_dv_i[6]; + +// Device 9 host connections (PLIC) + assign h2_dv_o[7] = sm1_s1n_10[0]; + assign h3_dv_o[7] = sm1_s1n_10[1]; + assign s1n_sm1_10[0] = h2_dv_i[7]; + assign s1n_sm1_10[1] = h3_dv_i[7]; + + +// hostv 1 connections + assign brqifu_to_s1n = tl_brqif_i; + assign tl_brqif_o = s1n_to_brqifu; +// hostv 2 connections + assign brqlsu_to_s1n = tl_brqlsu_i; + assign tl_brqlsu_o = s1n_to_brqlsu; +// host 3 connections + assign dbg_to_s1n = tl_dm_sba_i; + assign tl_dm_sba_o = s1n_to_dbg; + +// host 1 device selection + always_comb begin + device_sel_1 = 2'd2; + if((brqifu_to_s1n.a_address & ~(ADDR_MASK_ICCM)) == ADDR_SPACE_ICCM) begin + device_sel_1 = 2'd0; + end else if ((brqifu_to_s1n.a_address & ~(ADDR_MASK_DEBUG_ROM)) == ADDR_SPACE_DEBUG_ROM) begin + device_sel_1 = 2'd1; + end + end + +// host 1 socket + tlul_socket_1n #( + .HReqDepth (4'h0), + .HRspDepth (4'h0), + .DReqDepth (12'h0), + .DRspDepth (12'h0), + .N (2) + ) host_1 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (brqifu_to_s1n), + .tl_h_o (s1n_to_brqifu), + .tl_d_o (h1_dv_i), + .tl_d_i (h1_dv_o), + .dev_select_i (device_sel_1) + ); + +// host 2 socket + always_comb begin + + device_sel_2 = 4'd9; + + if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_DCCM)) == ADDR_SPACE_DCCM) begin + device_sel_2 = 4'd0; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_DEBUG_ROM)) == ADDR_SPACE_DEBUG_ROM) begin + device_sel_2 = 4'd1; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_TIMER0)) == ADDR_SPACE_TIMER0) begin + device_sel_2 = 4'd2; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_UART0)) == ADDR_SPACE_UART0) begin + device_sel_2 = 4'd3; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_SPI0)) == ADDR_SPACE_SPI0) begin + device_sel_2 = 4'd4; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_PWM)) == ADDR_SPACE_PWM) begin + device_sel_2 = 4'd5; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_GPIO)) == ADDR_SPACE_GPIO) begin + device_sel_2 = 4'd6; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_PLIC)) == ADDR_SPACE_PLIC) begin + device_sel_2 = 4'd7; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_ICCM)) == ADDR_SPACE_ICCM) begin + device_sel_2 = 4'd8; + end + end + +// host 2 socket + + tlul_socket_1n #( + .HReqDepth (4'h0), + .HRspDepth (4'h0), + .DReqDepth (36'h0), + .DRspDepth (36'h0), + .N (9) + ) host_2 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (brqlsu_to_s1n), + .tl_h_o (s1n_to_brqlsu), + .tl_d_o (h2_dv_i), + .tl_d_i (h2_dv_o), + .dev_select_i (device_sel_2) + ); + +// host 3 device selection + + always_comb begin + + device_sel_3 = 4'd8; + + if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_DCCM)) == ADDR_SPACE_DCCM) begin + device_sel_3 = 4'd0; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_ICCM)) == ADDR_SPACE_ICCM) begin + device_sel_3 = 4'd1; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_TIMER0)) == ADDR_SPACE_TIMER0) begin + device_sel_3 = 4'd2; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_UART0)) == ADDR_SPACE_UART0) begin + device_sel_3 = 4'd3; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_SPI0)) == ADDR_SPACE_SPI0) begin + device_sel_3 = 4'd4; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_PWM)) == ADDR_SPACE_PWM) begin + device_sel_3 = 4'd5; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_GPIO)) == ADDR_SPACE_GPIO) begin + device_sel_3 = 4'd6; + end else if ((brqlsu_to_s1n.a_address & ~(ADDR_MASK_PLIC)) == ADDR_SPACE_PLIC) begin + device_sel_3 = 4'd7; + end + end + + tlul_socket_1n #( + .HReqDepth (4'h0), + .HRspDepth (4'h0), + .DReqDepth (36'h0), + .DRspDepth (36'h0), + .N (8) + ) host_3 ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (dbg_to_s1n), + .tl_h_o (s1n_to_dbg), + .tl_d_o (h3_dv_i), + .tl_d_i (h3_dv_o), + .dev_select_i (device_sel_3) + ); + + +// Devices + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (3) + ) ICCM ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_1), + .tl_h_o (sm1_s1n_1), + .tl_d_o (tl_iccm_o), + .tl_d_i (tl_iccm_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) DCCM ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_2), + .tl_h_o (sm1_s1n_2), + .tl_d_o (tl_dccm_o), + .tl_d_i (tl_dccm_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) DEBUG_ROM ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_4), + .tl_h_o (sm1_s1n_4), + .tl_d_o (tl_debug_rom_o), + .tl_d_i (tl_debug_rom_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) TIMER ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_5), + .tl_h_o (sm1_s1n_5), + .tl_d_o (tl_timer0_o), + .tl_d_i (tl_timer0_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) UART ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_6), + .tl_h_o (sm1_s1n_6), + .tl_d_o (tl_uart_o), + .tl_d_i (tl_uart_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) SPI ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_7), + .tl_h_o (sm1_s1n_7), + .tl_d_o (tl_spi_o), + .tl_d_i (tl_spi_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) PWM ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_8), + .tl_h_o (sm1_s1n_8), + .tl_d_o (tl_pwm_o), + .tl_d_i (tl_pwm_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) GPIO ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_9), + .tl_h_o (sm1_s1n_9), + .tl_d_o (tl_gpio_o), + .tl_d_i (tl_gpio_i) + ); + + tlul_socket_m1 #( + .HReqDepth (8'h0), + .HRspDepth (8'h0), + .DReqDepth (4'h0), + .DRspDepth (4'h0), + .M (2) + ) PLIC ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (s1n_sm1_10), + .tl_h_o (sm1_s1n_10), + .tl_d_o (tl_plic_o), + .tl_d_i (tl_plic_i) + ); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/tlul_adapter_reg.sv b/verilog/rtl/tlul_adapter_reg.sv new file mode 100644 index 0000000..c801bc5 --- /dev/null +++ b/verilog/rtl/tlul_adapter_reg.sv
@@ -0,0 +1,125 @@ +module tlul_adapter_reg import tlul_pkg::*; #( + parameter int RegAw = 8, + parameter int RegDw = 32, // Shall be matched with TL_DW + localparam int RegBw = RegDw/8 +) ( + input clk_i, + input rst_ni, + + // TL-UL interface + input tl_h2d_t tl_i, + output tl_d2h_t tl_o, + + // Register interface + output logic re_o, + output logic we_o, + output logic [RegAw-1:0] addr_o, + output logic [RegDw-1:0] wdata_o, + output logic [RegBw-1:0] be_o, + input [RegDw-1:0] rdata_i, + input error_i +); + + localparam int IW = $bits(tl_i.a_source); + localparam int SZW = $bits(tl_i.a_size); + + logic outstanding; // Indicates current request is pending + logic a_ack, d_ack; + + logic [RegDw-1:0] rdata; + logic error, err_internal; + + logic addr_align_err; // Size and alignment +// logic malformed_meta_err; // User signal format error or unsupported + logic tl_err; // Common TL-UL error checker + + logic [IW-1:0] reqid; + logic [SZW-1:0] reqsz; + tlul_pkg::tl_d_m_op rspop; + + logic rd_req, wr_req; + + assign a_ack = tl_i.a_valid & tl_o.a_ready; + assign d_ack = tl_o.d_valid & tl_i.d_ready; + // Request signal + assign wr_req = a_ack & ((tl_i.a_opcode == PutFullData) | (tl_i.a_opcode == PutPartialData)); + assign rd_req = a_ack & (tl_i.a_opcode == Get); + + assign we_o = wr_req & ~err_internal; + assign re_o = rd_req & ~err_internal; + assign addr_o = {tl_i.a_address[RegAw-1:2], 2'b00}; // generate always word-align + assign wdata_o = tl_i.a_data; + assign be_o = tl_i.a_mask; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) outstanding <= 1'b0; + else if (a_ack) outstanding <= 1'b1; + else if (d_ack) outstanding <= 1'b0; + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + reqid <= '0; + reqsz <= '0; + rspop <= AccessAck; + end else if (a_ack) begin + reqid <= tl_i.a_source; + reqsz <= tl_i.a_size; + // Return AccessAckData regardless of error + rspop <= (rd_req) ? AccessAckData : AccessAck ; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rdata <= '0; + error <= 1'b0; + end else if (a_ack) begin + rdata <= (err_internal) ? '1 : rdata_i; + error <= error_i | err_internal; + end + end + + assign tl_o = '{ + a_ready: ~outstanding, + d_valid: outstanding, + d_opcode: rspop, + d_param: '0, + d_size: reqsz, + d_source: reqid, + d_sink: '0, + d_data: rdata, + d_error: error + }; + + //////////////////// + // Error Handling // + //////////////////// + assign err_internal = addr_align_err | tl_err ; + + // malformed_meta_err + // Raised if not supported feature is turned on or user signal has malformed + // assign malformed_meta_err = (tl_i.a_user.parity_en == 1'b1); + + // addr_align_err + // Raised if addr isn't aligned with the size + // Read size error is checked in tlul_assert.sv + // Here is it added due to the limitation of register interface. + always_comb begin + if (wr_req) begin + // Only word-align is accepted based on comportability spec + addr_align_err = |tl_i.a_address[1:0]; + end else begin + // No request + addr_align_err = 1'b0; + end + end + + // tl_err : separate checker + tlul_err u_err ( + .tl_i (tl_i), + .err_o (tl_err) + ); + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/tlul_err.sv b/verilog/rtl/tlul_err.sv new file mode 100644 index 0000000..32d7b67 --- /dev/null +++ b/verilog/rtl/tlul_err.sv
@@ -0,0 +1,86 @@ + + +module tlul_err import tlul_pkg::*; ( + + input tl_h2d_t tl_i, + + output logic err_o +); + + localparam int IW = $bits(tl_i.a_source); + localparam int SZW = $bits(tl_i.a_size); + localparam int DW = $bits(tl_i.a_data); + localparam int MW = $bits(tl_i.a_mask); + localparam int SubAW = $clog2(DW/8); + + logic opcode_allowed, a_config_allowed; + + logic op_full, op_partial, op_get; + assign op_full = (tl_i.a_opcode == PutFullData); + assign op_partial = (tl_i.a_opcode == PutPartialData); + assign op_get = (tl_i.a_opcode == Get); + + // Anything that doesn't fall into the permitted category, it raises an error + assign err_o = ~(opcode_allowed & a_config_allowed); + + // opcode check + assign opcode_allowed = (tl_i.a_opcode == PutFullData) + | (tl_i.a_opcode == PutPartialData) + | (tl_i.a_opcode == Get); + + // a channel configuration check + logic addr_sz_chk; // address and size alignment check + logic mask_chk; // inactive lane a_mask check + logic fulldata_chk; // PutFullData should have size match to mask + + logic [MW-1:0] mask; + + assign mask = (1 << tl_i.a_address[SubAW-1:0]); + + always_comb begin + addr_sz_chk = 1'b0; + mask_chk = 1'b0; + fulldata_chk = 1'b0; // Only valid when opcode is PutFullData + + if (tl_i.a_valid) begin + unique case (tl_i.a_size) + 'h0: begin // 1 Byte + addr_sz_chk = 1'b1; + mask_chk = ~|(tl_i.a_mask & ~mask); + fulldata_chk = |(tl_i.a_mask & mask); + end + + 'h1: begin // 2 Byte + addr_sz_chk = ~tl_i.a_address[0]; + // check inactive lanes if lower 2B, check a_mask[3:2], if uppwer 2B, a_mask[1:0] + mask_chk = (tl_i.a_address[1]) ? ~|(tl_i.a_mask & 4'b0011) + : ~|(tl_i.a_mask & 4'b1100); + fulldata_chk = (tl_i.a_address[1]) ? &tl_i.a_mask[3:2] : &tl_i.a_mask[1:0] ; + end + + 'h2: begin // 4 Byte + addr_sz_chk = ~|tl_i.a_address[SubAW-1:0]; + mask_chk = 1'b1; + fulldata_chk = &tl_i.a_mask[3:0]; + end + + default: begin // else + addr_sz_chk = 1'b0; + mask_chk = 1'b0; + fulldata_chk = 1'b0; + end + endcase + end else begin + addr_sz_chk = 1'b0; + mask_chk = 1'b0; + fulldata_chk = 1'b0; + end + end + + assign a_config_allowed = addr_sz_chk + & mask_chk + & (op_get | op_partial | fulldata_chk) ; + + +endmodule +
diff --git a/verilog/rtl/tlul_err_resp.sv b/verilog/rtl/tlul_err_resp.sv new file mode 100644 index 0000000..bd5a73c --- /dev/null +++ b/verilog/rtl/tlul_err_resp.sv
@@ -0,0 +1,56 @@ + +// TL-UL error responder module, used by tlul_socket_1n to help response +// to requests to no correct address space. Responses are always one cycle +// after request with no stalling unless response is stuck on the way out. +//`include "/home/sajjad/Shaheen-sv/src/buraq_core_top/ibex_core/tlul_pkg.sv" +module tlul_err_resp ( + input clk_i, + input rst_ni, + input tlul_pkg::tl_h2d_t tl_h_i, + output tlul_pkg::tl_d2h_t tl_h_o +); + import tlul_pkg::*; + + tlul_pkg::tl_a_m_op err_opcode; +// tlul_pkg::tl_a_m_op get; + logic [$bits(tl_h_i.a_source)-1:0] err_source; + logic [$bits(tl_h_i.a_size)-1:0] err_size; + logic err_req_pending, err_rsp_pending; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_req_pending <= 1'b0; + err_source <= {tlul_pkg::TL_AIW{1'b0}}; + err_opcode <= tlul_pkg::Get; + err_size <= '0; + end else if (tl_h_i.a_valid && tl_h_o.a_ready) begin + err_req_pending <= 1'b1; + err_source <= tl_h_i.a_source; + err_opcode <= tl_h_i.a_opcode; + err_size <= tl_h_i.a_size; + end else if (!err_rsp_pending) begin + err_req_pending <= 1'b0; + end + end + + assign tl_h_o.a_ready = ~err_rsp_pending & ~(err_req_pending & ~tl_h_i.d_ready); + assign tl_h_o.d_valid = err_req_pending | err_rsp_pending; + assign tl_h_o.d_data = '1; // Return all F + assign tl_h_o.d_source = err_source; + assign tl_h_o.d_sink = '0; + assign tl_h_o.d_param = '0; + assign tl_h_o.d_size = err_size; + assign tl_h_o.d_opcode = (err_opcode == tlul_pkg::Get) ? AccessAckData : AccessAck; + assign tl_h_o.d_error = 1'b1; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + err_rsp_pending <= 1'b0; + end else if ((err_req_pending || err_rsp_pending) && !tl_h_i.d_ready) begin + err_rsp_pending <= 1'b1; + end else begin + err_rsp_pending <= 1'b0; + end + end + +endmodule
diff --git a/verilog/rtl/tlul_fifo_sync.sv b/verilog/rtl/tlul_fifo_sync.sv new file mode 100644 index 0000000..917e059 --- /dev/null +++ b/verilog/rtl/tlul_fifo_sync.sv
@@ -0,0 +1,86 @@ + +// TL-UL fifo, used to add elasticity or an asynchronous clock crossing +// to an TL-UL bus. This instantiates two FIFOs, one for the request side, +// and one for the response side. + +module tlul_fifo_sync #( + parameter bit ReqPass = 1'b1, + parameter bit RspPass = 1'b1, + parameter int unsigned ReqDepth = 0, + parameter int unsigned RspDepth = 0, + parameter int unsigned SpareReqW = 1, + parameter int unsigned SpareRspW = 1 +) ( + input clk_i, + input rst_ni, + input tlul_pkg::tl_h2d_t tl_h_i, + output tlul_pkg::tl_d2h_t tl_h_o, + output tlul_pkg::tl_h2d_t tl_d_o, + input tlul_pkg::tl_d2h_t tl_d_i, + input [SpareReqW-1:0] spare_req_i, + output [SpareReqW-1:0] spare_req_o, + input [SpareRspW-1:0] spare_rsp_i, + output [SpareRspW-1:0] spare_rsp_o +); + + // Put everything on the request side into one FIFO + localparam int unsigned REQFIFO_WIDTH = $bits(tlul_pkg::tl_h2d_t) -2 + SpareReqW; + + fifo_sync #(.Width(REQFIFO_WIDTH), .Pass(ReqPass), .Depth(ReqDepth)) reqfifo ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .clr_i (1'b0 ), + .wvalid_i (tl_h_i.a_valid), + .wready_o (tl_h_o.a_ready), + .wdata_i ({tl_h_i.a_opcode , + tl_h_i.a_param , + tl_h_i.a_size , + tl_h_i.a_source , + tl_h_i.a_address, + tl_h_i.a_mask , + tl_h_i.a_data , + spare_req_i}), + .depth_o (), + .rvalid_o (tl_d_o.a_valid), + .rready_i (tl_d_i.a_ready), + .rdata_o ({tl_d_o.a_opcode , + tl_d_o.a_param , + tl_d_o.a_size , + tl_d_o.a_source , + tl_d_o.a_address, + tl_d_o.a_mask , + tl_d_o.a_data , + spare_req_o})); + + // Put everything on the response side into the other FIFO + + localparam int unsigned RSPFIFO_WIDTH = $bits(tlul_pkg::tl_d2h_t) -2 + SpareRspW; + + fifo_sync #(.Width(RSPFIFO_WIDTH), .Pass(RspPass), .Depth(RspDepth)) rspfifo ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .clr_i (1'b0 ), + .wvalid_i (tl_d_i.d_valid), + .wready_o (tl_d_o.d_ready), + .wdata_i ({tl_d_i.d_opcode, + tl_d_i.d_param , + tl_d_i.d_size , + tl_d_i.d_source, + tl_d_i.d_sink , + (tl_d_i.d_opcode == tlul_pkg::AccessAckData) ? tl_d_i.d_data : + {tlul_pkg::TL_DW{1'b0}} , + tl_d_i.d_error , + spare_rsp_i}), + .depth_o (), + .rvalid_o (tl_h_o.d_valid), + .rready_i (tl_h_i.d_ready), + .rdata_o ({tl_h_o.d_opcode, + tl_h_o.d_param , + tl_h_o.d_size , + tl_h_o.d_source, + tl_h_o.d_sink , + tl_h_o.d_data , + tl_h_o.d_error , + spare_rsp_o})); + +endmodule
diff --git a/verilog/rtl/tlul_host_adapter.sv b/verilog/rtl/tlul_host_adapter.sv new file mode 100644 index 0000000..8d18df5 --- /dev/null +++ b/verilog/rtl/tlul_host_adapter.sv
@@ -0,0 +1,95 @@ +// tlul_adapter (Host adapter) converts basic req/grant/rvalid into TL-UL interface. If +// MAX_REQS == 1 it is purely combinational logic. If MAX_REQS > 1 flops are required. +// +// The host driving the adapter is responsible for ensuring it doesn't have more requests in flight +// than the specified MAX_REQS. +// +// The outgoing address is always word aligned. The access size is always the word size (as +// specified by TL_DW). For write accesses that occupy all lanes the operation is PutFullData, +// otherwise it is PutPartialData, mask is generated from be_i. For reads all lanes are enabled as +// required by TL-UL (every bit in mask set). +// +// When MAX_REQS > 1 tlul_adapter_host does not do anything to order responses from the TL-UL +// interface which could return them out of order. It is the host's responsibility to either only +// have outstanding requests to an address space it knows will return responses in order or to not +// care about out of order responses (note that if read data is returned out of order there is no +// way to determine this). + +module tlul_host_adapter #( + parameter int unsigned MAX_REQS = 1 +) ( + input clk_i, + input rst_ni, +// interface with host agent + input req_i, + output logic gnt_o, + input logic [tlul_pkg::TL_AW-1:0] addr_i, + input logic we_i, + input logic [tlul_pkg::TL_DW-1:0] wdata_i, + input logic [tlul_pkg::TL_DBW-1:0] be_i, + output logic valid_o, + output logic [tlul_pkg::TL_DW-1:0] rdata_o, + output logic err_o, +// interface with other tilelink agents or tlul interface + output tlul_pkg::tl_h2d_t tl_h_c_a, // tilelink host channel A + input tlul_pkg::tl_d2h_t tl_h_c_d // tilelink host channel D +); + + localparam int WordSize = $clog2(tlul_pkg::TL_DBW); + + logic [tlul_pkg::TL_AIW-1:0] tl_source; + logic [tlul_pkg::TL_DBW-1:0] tl_be; + + if(MAX_REQS == 1) begin + assign tl_source = '0; + end else begin + localparam int ReqNumW = $clog2(MAX_REQS); + logic [ReqNumW-1:0] source_d, source_q; + + always_ff @(posedge clk_i) begin + if(!rst_ni) begin + source_q <= '0; + end else begin + source_q <= source_d; + end + end + + + always_comb begin + source_d = source_q; + + if(req_i && gnt_o) begin + if(source_q == MAX_REQS -1) source_d = '0; + else source_d = source_q + 1; + end + end + assign tl_source = tlul_pkg::TL_AIW'(source_q); + end + +// For TL-UL Get opcode all active bytes must have their mask bit set, so all reads get all tl_be +// bits set. For writes the supplied be_i is used as the mask. + assign tl_be = ~we_i ? {tlul_pkg::TL_DBW{1'b1}} : be_i; + + assign tl_h_c_a = '{ + a_valid: req_i, + a_opcode: (~we_i) ? tlul_pkg::Get : + (&be_i) ? tlul_pkg::PutFullData : + tlul_pkg::PutPartialData, + a_param: 3'h0, + a_size: tlul_pkg::TL_SZW'(WordSize), + a_mask: tl_be, + a_source: tl_source, + a_address: {addr_i[31:WordSize], {WordSize{1'b0}}}, + a_data: wdata_i, + d_ready: 1'b1 + }; + + assign gnt_o = tl_h_c_d.a_ready; + //assign rdata_0 = tl_h_c_d.d_data; + assign err_o = tl_h_c_d.d_error; + assign valid_o = tl_h_c_d.d_valid; + logic [31:0] rddata; + assign rddata = tl_h_c_d.d_data; + assign rdata_o = rddata; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/tlul_pkg.sv b/verilog/rtl/tlul_pkg.sv new file mode 100644 index 0000000..9da373f --- /dev/null +++ b/verilog/rtl/tlul_pkg.sv
@@ -0,0 +1,120 @@ +package tlul_pkg; + + + parameter ArbiterImpl = "PPC"; +function automatic integer _clog2(integer value); + integer result; + value = value - 1; + for (result = 0; value > 0; result = result + 1) begin + value = value >> 1; + end + return result; + endfunction + + + /** + * Math function: Number of bits needed to address |value| items. + * + * 0 for value == 0 + * vbits = 1 for value == 1 + * ceil(log2(value)) for value > 1 + * + * + * The primary use case for this function is the definition of registers/arrays + * which are wide enough to contain |value| items. + * + * This function identical to $clog2() for all input values except the value 1; + * it could be considered an "enhanced" $clog2() function. + * + * + * Example 1: + * parameter Items = 1; + * localparam ItemsWidth = vbits(Items); // 1 + * logic [ItemsWidth-1:0] item_register; // items_register is now [0:0] + * + * Example 2: + * parameter Items = 64; + * localparam ItemsWidth = vbits(Items); // 6 + * logic [ItemsWidth-1:0] item_register; // items_register is now [5:0] + * + * Note: If you want to store the number "value" inside a register, you need + * a register with size vbits(value + 1), since you also need to store + * the number 0. + * + * Example 3: + * logic [vbits(64)-1:0] store_64_logic_values; // width is [5:0] + * logic [vbits(64 + 1)-1:0] store_number_64; // width is [6:0] + */ + function automatic integer vbits(integer value); +`ifdef XCELIUM + // The use of system functions was not allowed here in Verilog-2001, but is + // valid since (System)Verilog-2005, which is also when $clog2() first + // appeared. + // Xcelium < 19.10 does not yet support the use of $clog2() here, fall back + // to an implementation without a system function. Remove this workaround + // if we require a newer Xcelium version. + // See #2579 and #2597. + return (value == 1) ? 1 : prim_util_pkg::_clog2(value); +`else + return (value == 1) ? 1 : $clog2(value); +`endif + endfunction + + localparam int TL_AW=32; + localparam int TL_DW=32; + localparam int TL_AIW=8; + localparam int TL_DIW=1; + localparam int TL_DBW=(TL_DW>>3); + localparam int TL_SZW=$clog2($clog2(TL_DBW)+1); + +// opcodes for channel D messages/operations defined in official TileLink spec + typedef enum logic [2:0] { + PutFullData = 3'h0, + PutPartialData = 3'h1, + Get = 3'h4 + } tl_a_m_op; +// opcodes for channel D messages/operations defined in official TileLink spec + typedef enum logic [2:0] { + AccessAck = 3'h0, + AccessAckData = 3'h1 + } tl_d_m_op; + + typedef struct packed { + logic a_valid; + tl_a_m_op a_opcode; + logic [2:0] a_param; + logic [TL_SZW-1:0] a_size; + logic [TL_AIW-1:0] a_source; + logic [TL_AW-1:0] a_address; + logic [TL_DBW-1:0] a_mask; + logic [TL_DW-1:0] a_data; + logic d_ready; + } tl_h2d_t; + + localparam tl_h2d_t TL_H2D_DEFAULT = '{ + d_ready: 1'b1, + a_opcode: tl_a_m_op'('0), + default: '0 + }; + + typedef struct packed { + logic d_valid; + tl_d_m_op d_opcode; + logic [2:0] d_param; + logic [TL_SZW-1:0] d_size; + logic [TL_AIW-1:0] d_source; + logic [TL_DIW-1:0] d_sink; + logic [TL_DW-1:0] d_data; + logic d_error; + logic a_ready; + } tl_d2h_t; + + localparam tl_d2h_t TL_D2H_DEFAULT = '{ + a_ready: 1'b1, + d_opcode: tl_d_m_op'('0), + default: '0 + }; + + + +endpackage
diff --git a/verilog/rtl/tlul_socket_1n.sv b/verilog/rtl/tlul_socket_1n.sv new file mode 100644 index 0000000..8ab6bdb --- /dev/null +++ b/verilog/rtl/tlul_socket_1n.sv
@@ -0,0 +1,209 @@ + +// TL-UL socket 1:N module +// +// configuration settings +// device_count: 4 +// +// Verilog parameters +// HReqPass: if 1 then host requests can pass through on empty fifo, +// default 1 +// HRspPass: if 1 then host responses can pass through on empty fifo, +// default 1 +// DReqPass: (one per device_count) if 1 then device i requests can +// pass through on empty fifo, default 1 +// DRspPass: (one per device_count) if 1 then device i responses can +// pass through on empty fifo, default 1 +// HReqDepth: Depth of host request FIFO, default 2 +// HRspDepth: Depth of host response FIFO, default 2 +// DReqDepth: (one per device_count) Depth of device i request FIFO, +// default 2 +// DRspDepth: (one per device_count) Depth of device i response FIFO, +// default 2 +// +// Requests must stall to one device until all responses from other devices +// have returned. Need to keep a counter of all outstanding requests and +// wait until that counter is zero before switching devices. +// +// This module will return a request error if the input value of 'dev_select_i' +// is not within the range 0..N-1. Thus the instantiator of the socket +// can indicate error by any illegal value of dev_select_i. 4'b1111 is +// recommended for visibility +// +// The maximum value of N is 15 + + +module tlul_socket_1n #( + parameter int unsigned N = 4, + parameter bit HReqPass = 1'b1, + parameter bit HRspPass = 1'b1, + parameter bit [N-1:0] DReqPass = {N{1'b1}}, + parameter bit [N-1:0] DRspPass = {N{1'b1}}, + parameter bit [3:0] HReqDepth = 4'h2, + parameter bit [3:0] HRspDepth = 4'h2, + parameter bit [N*4-1:0] DReqDepth = {N{4'h2}}, + parameter bit [N*4-1:0] DRspDepth = {N{4'h2}}, + localparam int unsigned NWD = $clog2(N+1) // derived parameter +) ( + input clk_i, + input rst_ni, + input tlul_pkg::tl_h2d_t tl_h_i, + output tlul_pkg::tl_d2h_t tl_h_o, + output tlul_pkg::tl_h2d_t tl_d_o [N], + input tlul_pkg::tl_d2h_t tl_d_i [N], + input [NWD-1:0] dev_select_i +); + + // Since our steering is done after potential FIFOing, we need to + // shove our device select bits into spare bits of reqfifo + + // instantiate the host fifo, create intermediate bus 't' + + // FIFO'd version of device select + logic [NWD-1:0] dev_select_t; + + tlul_pkg::tl_h2d_t tl_t_o; + tlul_pkg::tl_d2h_t tl_t_i; + + tlul_fifo_sync #( + .ReqPass(HReqPass), + .RspPass(HRspPass), + .ReqDepth(HReqDepth), + .RspDepth(HRspDepth), + .SpareReqW(NWD) + ) fifo_h ( + .clk_i, + .rst_ni, + .tl_h_i, + .tl_h_o, + .tl_d_o (tl_t_o), + .tl_d_i (tl_t_i), + .spare_req_i (dev_select_i), + .spare_req_o (dev_select_t), + .spare_rsp_i (1'b0), + .spare_rsp_o ()); + + + // We need to keep track of how many requests are outstanding, + // and to which device. New requests are compared to this and + // stall until that number is zero. + localparam int MaxOutstanding = 4**tlul_pkg::TL_AIW; // Up to 256 ounstanding + localparam int OutstandingW = $clog2(MaxOutstanding+1); + logic [OutstandingW-1:0] num_req_outstanding; + logic [NWD-1:0] dev_select_outstanding; + logic hold_all_requests; + logic accept_t_req, accept_t_rsp; + + assign accept_t_req = tl_t_o.a_valid & tl_t_i.a_ready; + assign accept_t_rsp = tl_t_i.d_valid & tl_t_o.d_ready; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + num_req_outstanding <= '0; + dev_select_outstanding <= '0; + end else if (accept_t_req) begin + if (!accept_t_rsp) begin + num_req_outstanding <= num_req_outstanding + 1'b1; + end + dev_select_outstanding <= dev_select_t; + end else if (accept_t_rsp) begin + num_req_outstanding <= num_req_outstanding - 1'b1; + end + end + + assign hold_all_requests = + (num_req_outstanding != '0) & + (dev_select_t != dev_select_outstanding); + + // Make N copies of 't' request side with modified reqvalid, call + // them 'u[0]' .. 'u[n-1]'. + + tlul_pkg::tl_h2d_t tl_u_o [N+1]; + tlul_pkg::tl_d2h_t tl_u_i [N+1]; + + for (genvar i = 0 ; i < N ; i++) begin : gen_u_o + assign tl_u_o[i].a_valid = tl_t_o.a_valid & + (dev_select_t == NWD'(i)) & + ~hold_all_requests; + assign tl_u_o[i].a_opcode = tl_t_o.a_opcode; + assign tl_u_o[i].a_param = tl_t_o.a_param; + assign tl_u_o[i].a_size = tl_t_o.a_size; + assign tl_u_o[i].a_source = tl_t_o.a_source; + assign tl_u_o[i].a_address = tl_t_o.a_address; + assign tl_u_o[i].a_mask = tl_t_o.a_mask; + assign tl_u_o[i].a_data = tl_t_o.a_data; + end + + tlul_pkg::tl_d2h_t tl_t_p ; + + // for the returning reqready, only look at the device we're addressing + logic hfifo_reqready; + always_comb begin + hfifo_reqready = tl_u_i[N].a_ready; // default to error + for (int idx = 0 ; idx < N ; idx++) begin + //if (dev_select_outstanding == NWD'(idx)) hfifo_reqready = tl_u_i[idx].a_ready; + if (dev_select_t == NWD'(idx)) hfifo_reqready = tl_u_i[idx].a_ready; + end + if (hold_all_requests) hfifo_reqready = 1'b0; + end + // Adding a_valid as a qualifier. This prevents the a_ready from having unknown value + // when the address is unknown and the Host TL-UL FIFO is bypass mode. + assign tl_t_i.a_ready = tl_t_o.a_valid & hfifo_reqready; + + always_comb begin + tl_t_p = tl_u_i[N]; + for (int idx = 0 ; idx < N ; idx++) begin + if (dev_select_outstanding == NWD'(idx)) tl_t_p = tl_u_i[idx]; + end + end + assign tl_t_i.d_valid = tl_t_p.d_valid ; + assign tl_t_i.d_opcode = tl_t_p.d_opcode; + assign tl_t_i.d_param = tl_t_p.d_param ; + assign tl_t_i.d_size = tl_t_p.d_size ; + assign tl_t_i.d_source = tl_t_p.d_source; + assign tl_t_i.d_sink = tl_t_p.d_sink ; + assign tl_t_i.d_data = tl_t_p.d_data ; + assign tl_t_i.d_error = tl_t_p.d_error ; + + + // accept responses from devices when selected if upstream is accepting + for (genvar i = 0 ; i < N+1 ; i++) begin : gen_u_o_d_ready + assign tl_u_o[i].d_ready = tl_t_o.d_ready; + end + + // finally instantiate all device FIFOs and the error responder + for (genvar i = 0 ; i < N ; i++) begin : gen_dfifo + tlul_fifo_sync #( + .ReqPass(DReqPass[i]), + .RspPass(DRspPass[i]), + .ReqDepth(DReqDepth[i*4+:4]), + .RspDepth(DRspDepth[i*4+:4]) + ) fifo_d ( + .clk_i, + .rst_ni, + .tl_h_i (tl_u_o[i]), + .tl_h_o (tl_u_i[i]), + .tl_d_o (tl_d_o[i]), + .tl_d_i (tl_d_i[i]), + .spare_req_i (1'b0), + .spare_req_o (), + .spare_rsp_i (1'b0), + .spare_rsp_o ()); + end + + assign tl_u_o[N].a_valid = tl_t_o.a_valid & + (dev_select_t == NWD'(N)) & + ~hold_all_requests; + assign tl_u_o[N].a_opcode = tl_t_o.a_opcode; + assign tl_u_o[N].a_param = tl_t_o.a_param; + assign tl_u_o[N].a_size = tl_t_o.a_size; + assign tl_u_o[N].a_source = tl_t_o.a_source; + assign tl_u_o[N].a_address = tl_t_o.a_address; + assign tl_u_o[N].a_mask = tl_t_o.a_mask; + assign tl_u_o[N].a_data = tl_t_o.a_data; + tlul_err_resp err_resp ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tl_h_i (tl_u_o[N]), + .tl_h_o (tl_u_i[N])); + +endmodule
diff --git a/verilog/rtl/tlul_socket_m1.sv b/verilog/rtl/tlul_socket_m1.sv new file mode 100644 index 0000000..2a9d335 --- /dev/null +++ b/verilog/rtl/tlul_socket_m1.sv
@@ -0,0 +1,245 @@ + +// TL-UL socket M:1 module +// +// Verilog parameters +// M: Number of host ports. +// HReqPass: M bit array to allow requests to pass through the host i +// FIFO with no clock delay if the request FIFO is empty. If +// 1'b0, at least one clock cycle of latency is created. +// Default is 1'b1. +// HRspPass: Same as HReqPass but for host response FIFO. +// HReqDepth: Mx4 bit array. bit[i*4+:4] is depth of host i request FIFO. +// Depth of zero is allowed if ReqPass is true. A maximum value +// of 16 is allowed, default is 2. +// HRspDepth: Same as HReqDepth but for host response FIFO. +// DReqPass: Same as HReqPass but for device request FIFO. +// DRspPass: Same as HReqPass but for device response FIFO. +// DReqDepth: Same as HReqDepth but for device request FIFO. +// DRspDepth: Same as HReqDepth but for device response FIFO. + +module tlul_socket_m1 #( + parameter int unsigned M = 4, + parameter bit [M-1:0] HReqPass = {M{1'b1}}, + parameter bit [M-1:0] HRspPass = {M{1'b1}}, + parameter bit [M*4-1:0] HReqDepth = {M{4'h2}}, + parameter bit [M*4-1:0] HRspDepth = {M{4'h2}}, + parameter bit DReqPass = 1'b1, + parameter bit DRspPass = 1'b1, + parameter bit [3:0] DReqDepth = 4'h2, + parameter bit [3:0] DRspDepth = 4'h2 +) ( + input clk_i, + input rst_ni, + + input tlul_pkg::tl_h2d_t tl_h_i [M], + output tlul_pkg::tl_d2h_t tl_h_o [M], + + output tlul_pkg::tl_h2d_t tl_d_o, + input tlul_pkg::tl_d2h_t tl_d_i +); + + // Signals + // + // tl_h_i/o[0] | tl_h_i/o[1] | ... | tl_h_i/o[M-1] + // | | | + // u_hostfifo[0] u_hostfifo[1] u_hostfifo[M-1] + // | | | + // hreq_fifo_o(i) / hrsp_fifo_i(i) + // --------------------------------------- + // | request/grant/req_data | + // | | + // | PRIM_ARBITER | + // | | + // | arb_valid / arb_ready / arb_data | + // --------------------------------------- + // | + // dreq_fifo_i / drsp_fifo_o + // | + // u_devicefifo + // | + // tl_d_o/i + // + // Required ID width to distinguish between host ports + // Used in response steering + localparam int unsigned IDW = tlul_pkg::TL_AIW; + localparam int unsigned STIDW = $clog2(M); + + tlul_pkg::tl_h2d_t hreq_fifo_o [M]; + tlul_pkg::tl_d2h_t hrsp_fifo_i [M]; + + logic [M-1:0] hrequest; + logic [M-1:0] hgrant; + + tlul_pkg::tl_h2d_t dreq_fifo_i; + tlul_pkg::tl_d2h_t drsp_fifo_o; + + logic arb_valid; + logic arb_ready; + tlul_pkg::tl_h2d_t arb_data; + + // Host Req/Rsp FIFO + for (genvar i = 0 ; i < M ; i++) begin : gen_host_fifo + tlul_pkg::tl_h2d_t hreq_fifo_i; + + // ID Shifting + logic [STIDW-1:0] reqid_sub; + logic [IDW-1:0] shifted_id; + assign reqid_sub = i; // can cause conversion error? + assign shifted_id = { + tl_h_i[i].a_source[0+:(IDW-STIDW)], + reqid_sub + }; + + + // assign not connected bits to nc_* signal to make lint happy + logic [IDW-1 : IDW-STIDW] unused_tl_h_source; + assign unused_tl_h_source = tl_h_i[i].a_source[IDW-1 -: STIDW]; + + // Put shifted ID + assign hreq_fifo_i = '{ + a_valid: tl_h_i[i].a_valid, + a_opcode: tl_h_i[i].a_opcode, + a_param: tl_h_i[i].a_param, + a_size: tl_h_i[i].a_size, + a_source: shifted_id, + a_address: tl_h_i[i].a_address, + a_mask: tl_h_i[i].a_mask, + a_data: tl_h_i[i].a_data, + d_ready: tl_h_i[i].d_ready + }; + + tlul_fifo_sync #( + .ReqPass (HReqPass[i]), + .RspPass (HRspPass[i]), + .ReqDepth (HReqDepth[i*4+:4]), + .RspDepth (HRspDepth[i*4+:4]), + .SpareReqW (1) + ) u_hostfifo ( + .clk_i, + .rst_ni, + .tl_h_i (hreq_fifo_i), + .tl_h_o (tl_h_o[i]), + .tl_d_o (hreq_fifo_o[i]), + .tl_d_i (hrsp_fifo_i[i]), + .spare_req_i (1'b0), + .spare_req_o (), + .spare_rsp_i (1'b0), + .spare_rsp_o () + ); + end + + // Device Req/Rsp FIFO + tlul_fifo_sync #( + .ReqPass (DReqPass), + .RspPass (DRspPass), + .ReqDepth (DReqDepth), + .RspDepth (DRspDepth), + .SpareReqW (1) + ) u_devicefifo ( + .clk_i, + .rst_ni, + .tl_h_i (dreq_fifo_i), + .tl_h_o (drsp_fifo_o), + .tl_d_o (tl_d_o), + .tl_d_i (tl_d_i), + .spare_req_i (1'b0), + .spare_req_o (), + .spare_rsp_i (1'b0), + .spare_rsp_o () + ); + + // Request Arbiter + for (genvar i = 0 ; i < M ; i++) begin : gen_arbreqgnt + assign hrequest[i] = hreq_fifo_o[i].a_valid; + end + + assign arb_ready = drsp_fifo_o.a_ready; + + if (tlul_pkg::ArbiterImpl == "PPC") begin : gen_arb_ppc + prim_arbiter_ppc #( + .N (M), + .DW ($bits(tlul_pkg::tl_h2d_t)), + .EnReqStabA (0) + ) u_reqarb ( + .clk_i, + .rst_ni, + .req_i ( hrequest ), + .data_i ( hreq_fifo_o ), + .gnt_o ( hgrant ), + .idx_o ( ), + .valid_o ( arb_valid ), + .data_o ( arb_data ), + .ready_i ( arb_ready ) + ); + end else if (tlul_pkg::ArbiterImpl == "BINTREE") begin : gen_tree_arb + prim_arbiter_tree #( + .N (M), + .DW ($bits(tlul_pkg::tl_h2d_t)), + .EnReqStabA (0) + ) u_reqarb ( + .clk_i, + .rst_ni, + .req_i ( hrequest ), + .data_i ( hreq_fifo_o ), + .gnt_o ( hgrant ), + .idx_o ( ), + .valid_o ( arb_valid ), + .data_o ( arb_data ), + .ready_i ( arb_ready ) + ); + end else begin : gen_unknown + + end + + logic [ M-1:0] hfifo_rspvalid; + logic [ M-1:0] dfifo_rspready; + logic [IDW-1:0] hfifo_rspid; + logic dfifo_rspready_merged; + + // arb_data --> dreq_fifo_i + // dreq_fifo_i.hd_rspready <= dfifo_rspready + + assign dfifo_rspready_merged = |dfifo_rspready; + assign dreq_fifo_i = '{ + a_valid: arb_valid, + a_opcode: arb_data.a_opcode, + a_param: arb_data.a_param, + a_size: arb_data.a_size, + a_source: arb_data.a_source, + a_address: arb_data.a_address, + a_mask: arb_data.a_mask, + a_data: arb_data.a_data, + + d_ready: dfifo_rspready_merged + }; + + // Response ID steering + // drsp_fifo_o --> hrsp_fifo_i[i] + + // Response ID shifting before put into host fifo + assign hfifo_rspid = { + {STIDW{1'b0}}, + drsp_fifo_o.d_source[IDW-1:STIDW] + }; + for (genvar i = 0 ; i < M ; i++) begin : gen_idrouting + assign hfifo_rspvalid[i] = drsp_fifo_o.d_valid & + (drsp_fifo_o.d_source[0+:STIDW] == i); + assign dfifo_rspready[i] = hreq_fifo_o[i].d_ready & + (drsp_fifo_o.d_source[0+:STIDW] == i) & + drsp_fifo_o.d_valid; + + assign hrsp_fifo_i[i] = '{ + d_valid: hfifo_rspvalid[i], + d_opcode: drsp_fifo_o.d_opcode, + d_param: drsp_fifo_o.d_param, + d_size: drsp_fifo_o.d_size, + d_source: hfifo_rspid, + d_sink: drsp_fifo_o.d_sink, + d_data: drsp_fifo_o.d_data, + d_error: drsp_fifo_o.d_error, + a_ready: hgrant[i] + }; + end + + +endmodule
diff --git a/verilog/rtl/tlul_sram_adapter.sv b/verilog/rtl/tlul_sram_adapter.sv new file mode 100644 index 0000000..dc77555 --- /dev/null +++ b/verilog/rtl/tlul_sram_adapter.sv
@@ -0,0 +1,338 @@ +/** + * Tile-Link UL adapter for SRAM-like devices + * + * - Intentionally omitted BaseAddr in case of multiple memory maps are used in a SoC, + * it means that aliasing can happen if target device size in TL-UL crossbar is bigger + * than SRAM size + */ +module tlul_sram_adapter #( + parameter int SramAw = 12, + parameter int SramDw = 32, // Must be multiple of the TL width + parameter int Outstanding = 1, // Only one request is accepted + parameter bit ByteAccess = 1, // 1: true, 0: false + parameter bit ErrOnWrite = 0, // 1: Writes not allowed, automatically error + parameter bit ErrOnRead = 0 // 1: Reads not allowed, automatically error +) ( + input clk_i, + input rst_ni, + + // TL-UL interface + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + // SRAM interface + output logic req_o, + input gnt_i, + output logic we_o, + output logic [SramAw-1:0] addr_o, + output logic [SramDw-1:0] wdata_o, + output logic [SramDw-1:0] wmask_o, + input [SramDw-1:0] rdata_i, + input rvalid_i, + input [1:0] rerror_i // 2 bit error [1]: Uncorrectable, [0]: Correctable +); + + import tlul_pkg::*; + + localparam int SramByte = SramDw/8; + localparam int DataBitWidth = tlul_pkg::vbits(SramByte); + localparam int WidthMult = SramDw / tlul_pkg::TL_DW; + localparam int WoffsetWidth = (SramByte == tlul_pkg::TL_DBW) ? 1 : + DataBitWidth - tlul_pkg::vbits(tlul_pkg::TL_DBW); + + typedef struct packed { + logic [tlul_pkg::TL_DBW-1:0] mask ; // Byte mask within the TL-UL word + logic [WoffsetWidth-1:0] woffset ; // Offset of the TL-UL word within the SRAM word + } sram_req_t ; + + typedef enum logic [1:0] { + OpWrite, + OpRead, + OpUnknown + } req_op_e ; + + typedef struct packed { + req_op_e op ; + logic error ; + logic [tlul_pkg::TL_SZW-1:0] size ; + logic [tlul_pkg::TL_AIW-1:0] source ; + } req_t ; + + typedef struct packed { + logic [SramDw-1:0] data ; + logic error ; + } rsp_t ; + + localparam int SramReqFifoWidth = $bits(sram_req_t) ; + localparam int ReqFifoWidth = $bits(req_t) ; + localparam int RspFifoWidth = $bits(rsp_t) ; + + // FIFO signal in case OutStand is greater than 1 + // If request is latched, {write, source} is pushed to req fifo. + // Req fifo is popped when D channel is acknowledged (v & r) + // D channel valid is asserted if it is write request or rsp fifo not empty if read. + logic reqfifo_wvalid, reqfifo_wready; + logic reqfifo_rvalid, reqfifo_rready; + req_t reqfifo_wdata, reqfifo_rdata; + + logic sramreqfifo_wvalid, sramreqfifo_wready; + logic sramreqfifo_rready; + sram_req_t sramreqfifo_wdata, sramreqfifo_rdata; + + logic rspfifo_wvalid, rspfifo_wready; + logic rspfifo_rvalid, rspfifo_rready; + rsp_t rspfifo_wdata, rspfifo_rdata; + + logic error_internal; // Internal protocol error checker + logic wr_attr_error; + logic wr_vld_error; + logic rd_vld_error; + logic tlul_error; // Error from `tlul_err` module + + logic a_ack, d_ack, sram_ack; + assign a_ack = tl_i.a_valid & tl_o.a_ready ; + assign d_ack = tl_o.d_valid & tl_i.d_ready ; + assign sram_ack = req_o & gnt_i ; + + // Valid handling + logic d_valid, d_error; + always_comb begin + d_valid = 1'b0; + + if (reqfifo_rvalid) begin + if (reqfifo_rdata.error) begin + // Return error response. Assume no request went out to SRAM + d_valid = 1'b1; + end else if (reqfifo_rdata.op == OpRead) begin + d_valid = rspfifo_rvalid; + end else begin + // Write without error + d_valid = 1'b1; + end + end else begin + d_valid = 1'b0; + end + end + + always_comb begin + d_error = 1'b0; + + if (reqfifo_rvalid) begin + if (reqfifo_rdata.op == OpRead) begin + d_error = rspfifo_rdata.error | reqfifo_rdata.error; + end else begin + d_error = reqfifo_rdata.error; + end + end else begin + d_error = 1'b0; + end + end + + assign tl_o = '{ + d_valid : d_valid , + d_opcode : (d_valid && reqfifo_rdata.op != OpRead) ? AccessAck : AccessAckData, + d_param : '0, + d_size : (d_valid) ? reqfifo_rdata.size : '0, + d_source : (d_valid) ? reqfifo_rdata.source : '0, + d_sink : 1'b0, + d_data : (d_valid && rspfifo_rvalid && reqfifo_rdata.op == OpRead) + ? rspfifo_rdata.data : '0, + d_error : d_valid && d_error, + + a_ready : (gnt_i | error_internal) & reqfifo_wready & sramreqfifo_wready + }; + + // a_ready depends on the FIFO full condition and grant from SRAM (or SRAM arbiter) + // assemble response, including read response, write response, and error for unsupported stuff + + // Output to SRAM: + // Generate request only when no internal error occurs. If error occurs, the request should be + // dropped and returned error response to the host. So, error to be pushed to reqfifo. + // In this case, it is assumed the request is granted (may cause ordering issue later?) + assign req_o = tl_i.a_valid & reqfifo_wready & ~error_internal; + assign we_o = tl_i.a_valid & logic'(tl_i.a_opcode inside {PutFullData, PutPartialData}); + assign addr_o = (tl_i.a_valid) ? tl_i.a_address[DataBitWidth+:SramAw] : '0; + + // Support SRAMs wider than the TL-UL word width by mapping the parts of the + // TL-UL address which are more fine-granular than the SRAM width to the + // SRAM write mask. + logic [WoffsetWidth-1:0] woffset; + if (tlul_pkg::TL_DW != SramDw) begin : gen_wordwidthadapt + assign woffset = tl_i.a_address[DataBitWidth-1:tlul_pkg::vbits(tlul_pkg::TL_DBW)]; + end else begin : gen_no_wordwidthadapt + assign woffset = '0; + end + + // Convert byte mask to SRAM bit mask for writes, and only forward valid data + logic [WidthMult-1:0][tlul_pkg::TL_DW-1:0] wmask_int; + logic [WidthMult-1:0][tlul_pkg::TL_DW-1:0] wdata_int; + + always_comb begin + wmask_int = '0; + wdata_int = '0; + + if (tl_i.a_valid) begin + for (int i = 0 ; i < tlul_pkg::TL_DW/8 ; i++) begin + wmask_int[woffset][8*i +: 8] = {8{tl_i.a_mask[i]}}; + wdata_int[woffset][8*i +: 8] = (tl_i.a_mask[i] && we_o) ? tl_i.a_data[8*i+:8] : '0; + end + end + end + + assign wmask_o = wmask_int; + assign wdata_o = wdata_int; + + // Begin: Request Error Detection + + // wr_attr_error: Check if the request size,mask are permitted. + // Basic check of size, mask, addr align is done in tlul_err module. + // Here it checks any partial write if ByteAccess isn't allowed. + assign wr_attr_error = (tl_i.a_opcode == PutFullData || tl_i.a_opcode == PutPartialData) ? + (ByteAccess == 0) ? (tl_i.a_mask != '1 || tl_i.a_size != 2'h2) : 1'b0 : + 1'b0; + + if (ErrOnWrite == 1) begin : gen_no_writes + assign wr_vld_error = tl_i.a_opcode != Get; + end else begin : gen_writes_allowed + assign wr_vld_error = 1'b0; + end + + if (ErrOnRead == 1) begin: gen_no_reads + assign rd_vld_error = tl_i.a_opcode == Get; + end else begin : gen_reads_allowed + assign rd_vld_error = 1'b0; + end + + tlul_err u_err ( + .tl_i (tl_i), + .err_o (tlul_error) + ); + + assign error_internal = wr_attr_error | wr_vld_error | rd_vld_error | tlul_error; + // End: Request Error Detection + + assign reqfifo_wvalid = a_ack ; // Push to FIFO only when granted + assign reqfifo_wdata = '{ + op: (tl_i.a_opcode != Get) ? OpWrite : OpRead, // To return AccessAck for opcode error + error: error_internal, + size: tl_i.a_size, + source: tl_i.a_source + }; // Store the request only. Doesn't have to store data + assign reqfifo_rready = d_ack ; + + // push together with ReqFIFO, pop upon returning read + assign sramreqfifo_wdata = '{ + mask : tl_i.a_mask, + woffset : woffset + }; + assign sramreqfifo_wvalid = sram_ack & ~we_o; + assign sramreqfifo_rready = rspfifo_wvalid; + + assign rspfifo_wvalid = rvalid_i & reqfifo_rvalid; + + // Make sure only requested bytes are forwarded + logic [WidthMult-1:0][tlul_pkg::TL_DW-1:0] rdata; + logic [WidthMult-1:0][tlul_pkg::TL_DW-1:0] rmask; + //logic [SramDw-1:0] rmask; + logic [tlul_pkg::TL_DW-1:0] rdata_tlword; + + always_comb begin + rmask = '0; + for (int i = 0 ; i < tlul_pkg::TL_DW/8 ; i++) begin + rmask[sramreqfifo_rdata.woffset][8*i +: 8] = {8{sramreqfifo_rdata.mask[i]}}; + end + end + + assign rdata = rdata_i & rmask; + assign rdata_tlword = rdata[sramreqfifo_rdata.woffset]; + + assign rspfifo_wdata = '{ + data : rdata_tlword, + error: rerror_i[1] // Only care for Uncorrectable error + }; + assign rspfifo_rready = (reqfifo_rdata.op == OpRead & ~reqfifo_rdata.error) + ? reqfifo_rready : 1'b0 ; + + // This module only cares about uncorrectable errors. + logic unused_rerror; + assign unused_rerror = rerror_i[0]; + + // FIFO instance: REQ, RSP + + // ReqFIFO is to store the Access type to match to the Response data. + // For instance, SRAM accepts the write request but doesn't return the + // acknowledge. In this case, it may be hard to determine when the D + // response for the write data should send out if reads/writes are + // interleaved. So, to make it in-order (even TL-UL allows out-of-order + // responses), storing the request is necessary. And if the read entry + // is write op, it is safe to return the response right away. If it is + // read reqeust, then D response is waiting until read data arrives. + + // Notes: + // The oustanding+1 allows the reqfifo to absorb back to back transactions + // without any wait states. Alternatively, the depth can be kept as + // oustanding as long as the outgoing ready is qualified with the acceptance + // of the response in the same cycle. Doing so however creates a path from + // ready_i to ready_o, which may not be desireable. + fifo_sync #( + .Width (ReqFifoWidth), + .Pass (1'b0), + .Depth (Outstanding) + ) u_reqfifo ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(reqfifo_wvalid), + .wready_o(reqfifo_wready), + .wdata_i (reqfifo_wdata), + .depth_o (), + .rvalid_o(reqfifo_rvalid), + .rready_i(reqfifo_rready), + .rdata_o (reqfifo_rdata) + ); + + // sramreqfifo: + // While the ReqFIFO holds the request until it is sent back via TL-UL, the + // sramreqfifo only needs to hold the mask and word offset until the read + // data returns from memory. + fifo_sync #( + .Width (SramReqFifoWidth), + .Pass (1'b0), + .Depth (Outstanding) + ) u_sramreqfifo ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(sramreqfifo_wvalid), + .wready_o(sramreqfifo_wready), + .wdata_i (sramreqfifo_wdata), + .depth_o (), + .rvalid_o(), + .rready_i(sramreqfifo_rready), + .rdata_o (sramreqfifo_rdata) + ); + + // Rationale having #Outstanding depth in response FIFO. + // In normal case, if the host or the crossbar accepts the response data, + // response FIFO isn't needed. But if in any case it has a chance to be + // back pressured, the response FIFO should store the returned data not to + // lose the data from the SRAM interface. Remember, SRAM interface doesn't + // have back-pressure signal such as read_ready. + fifo_sync #( + .Width (RspFifoWidth), + .Pass (1'b1), + .Depth (Outstanding) + ) u_rspfifo ( + .clk_i, + .rst_ni, + .clr_i (1'b0), + .wvalid_i(rspfifo_wvalid), + .wready_o(rspfifo_wready), + .wdata_i (rspfifo_wdata), + .depth_o (), + .rvalid_o(rspfifo_rvalid), + .rready_i(rspfifo_rready), + .rdata_o (rspfifo_rdata) + ); + +endmodule
diff --git a/verilog/rtl/uart.sv b/verilog/rtl/uart.sv new file mode 100644 index 0000000..090b02b --- /dev/null +++ b/verilog/rtl/uart.sv
@@ -0,0 +1,85 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Description: UART top level wrapper file + +// `include "prim_assert.sv" + +module uart ( + input clk_i, + input rst_ni, + + // Bus Interface + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + + // Generic IO + input cio_rx_i, + output logic cio_tx_o, + output logic cio_tx_en_o, + + // Interrupts + output logic intr_tx_watermark_o , + output logic intr_rx_watermark_o , + output logic intr_tx_empty_o , + output logic intr_rx_overflow_o , + output logic intr_rx_frame_err_o , + output logic intr_rx_break_err_o , + output logic intr_rx_timeout_o , + output logic intr_rx_parity_err_o +); + + import uart_reg_pkg::*; + + uart_reg2hw_t reg2hw; + uart_hw2reg_t hw2reg; + + uart_reg_top u_reg ( + .clk_i, + .rst_ni, + .tl_i, + .tl_o, + .reg2hw, + .hw2reg, + + .devmode_i (1'b1) + ); + + uart_core uart_core ( + .clk_i, + .rst_ni, + .reg2hw, + .hw2reg, + + .rx (cio_rx_i ), + .tx (cio_tx_o ), + + .intr_tx_watermark_o, + .intr_rx_watermark_o, + .intr_tx_empty_o, + .intr_rx_overflow_o, + .intr_rx_frame_err_o, + .intr_rx_break_err_o, + .intr_rx_timeout_o, + .intr_rx_parity_err_o + ); + + // always enable the driving out of TX + assign cio_tx_en_o = 1'b1; + + // // Assert Known for outputs + // `ASSERT_KNOWN(txenKnown, cio_tx_en_o) + // `ASSERT_KNOWN(txKnown, cio_tx_o, clk_i, !rst_ni || !cio_tx_en_o) + + // // Assert Known for interrupts + // `ASSERT_KNOWN(txWatermarkKnown, intr_tx_watermark_o) + // `ASSERT_KNOWN(rxWatermarkKnown, intr_rx_watermark_o) + // `ASSERT_KNOWN(txEmptyKnown, intr_tx_empty_o) + // `ASSERT_KNOWN(rxOverflowKnown, intr_rx_overflow_o) + // `ASSERT_KNOWN(rxFrameErrKnown, intr_rx_frame_err_o) + // `ASSERT_KNOWN(rxBreakErrKnown, intr_rx_break_err_o) + // `ASSERT_KNOWN(rxTimeoutKnown, intr_rx_timeout_o) + // `ASSERT_KNOWN(rxParityErrKnown, intr_rx_parity_err_o) + +endmodule
diff --git a/verilog/rtl/uart_core.sv b/verilog/rtl/uart_core.sv new file mode 100644 index 0000000..c205d90 --- /dev/null +++ b/verilog/rtl/uart_core.sv
@@ -0,0 +1,490 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Description: UART core module +// + +module uart_core ( + input clk_i, + input rst_ni, + + input uart_reg_pkg::uart_reg2hw_t reg2hw, + output uart_reg_pkg::uart_hw2reg_t hw2reg, + + input rx, + output logic tx, + + output logic intr_tx_watermark_o, + output logic intr_rx_watermark_o, + output logic intr_tx_empty_o, + output logic intr_rx_overflow_o, + output logic intr_rx_frame_err_o, + output logic intr_rx_break_err_o, + output logic intr_rx_timeout_o, + output logic intr_rx_parity_err_o +); + + import uart_reg_pkg::*; + + localparam int NcoWidth = $bits(reg2hw.ctrl.nco.q); + + logic [15:0] rx_val_q; + logic [7:0] uart_rdata; + logic tick_baud_x16, rx_tick_baud; + logic [5:0] tx_fifo_depth, rx_fifo_depth; + logic [5:0] rx_fifo_depth_prev_q; + logic [23:0] rx_timeout_count_d, rx_timeout_count_q, uart_rxto_val; + logic rx_fifo_depth_changed, uart_rxto_en; + logic tx_enable, rx_enable; + logic sys_loopback, line_loopback, rxnf_enable; + logic uart_fifo_rxrst, uart_fifo_txrst; + logic [2:0] uart_fifo_rxilvl; + logic [1:0] uart_fifo_txilvl; + logic ovrd_tx_en, ovrd_tx_val; + logic [7:0] tx_fifo_data; + logic tx_fifo_rready, tx_fifo_rvalid; + logic tx_fifo_wready, tx_uart_idle; + logic tx_out; + logic tx_out_q; + logic [7:0] rx_fifo_data; + logic rx_valid, rx_fifo_wvalid, rx_fifo_rvalid; + logic rx_fifo_wready, rx_uart_idle; + logic rx_sync; + logic rx_in; + logic break_err; + logic [4:0] allzero_cnt_d, allzero_cnt_q; + logic allzero_err, not_allzero_char; + logic event_tx_watermark, event_rx_watermark, event_tx_empty, event_rx_overflow; + logic event_rx_frame_err, event_rx_break_err, event_rx_timeout, event_rx_parity_err; + logic tx_watermark_d, tx_watermark_prev_q; + logic rx_watermark_d, rx_watermark_prev_q; + logic tx_uart_idle_q; + + assign tx_enable = reg2hw.ctrl.tx.q; + assign rx_enable = reg2hw.ctrl.rx.q; + assign rxnf_enable = reg2hw.ctrl.nf.q; + assign sys_loopback = reg2hw.ctrl.slpbk.q; + assign line_loopback = reg2hw.ctrl.llpbk.q; + + assign uart_fifo_rxrst = reg2hw.fifo_ctrl.rxrst.q & reg2hw.fifo_ctrl.rxrst.qe; + assign uart_fifo_txrst = reg2hw.fifo_ctrl.txrst.q & reg2hw.fifo_ctrl.txrst.qe; + assign uart_fifo_rxilvl = reg2hw.fifo_ctrl.rxilvl.q; + assign uart_fifo_txilvl = reg2hw.fifo_ctrl.txilvl.q; + + assign ovrd_tx_en = reg2hw.ovrd.txen.q; + assign ovrd_tx_val = reg2hw.ovrd.txval.q; + + typedef enum logic { + BRK_CHK, + BRK_WAIT + } break_st_e ; + + break_st_e break_st_q; + + assign not_allzero_char = rx_valid & (~event_rx_frame_err | (rx_fifo_data != 8'h0)); + assign allzero_err = event_rx_frame_err & (rx_fifo_data == 8'h0); + + + assign allzero_cnt_d = (break_st_q == BRK_WAIT || not_allzero_char) ? 5'h0 : + //allzero_cnt_q[4] never be 1b without break_st_q as BRK_WAIT + //allzero_cnt_q[4] ? allzero_cnt_q : + allzero_err ? allzero_cnt_q + 5'd1 : + allzero_cnt_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) allzero_cnt_q <= '0; + else if (rx_enable) allzero_cnt_q <= allzero_cnt_d; + end + + // break_err edges in same cycle as event_rx_frame_err edges ; that way the + // reset-on-read works the same way for break and frame error interrupts. + + always_comb begin + unique case (reg2hw.ctrl.rxblvl.q) + 2'h0: break_err = allzero_cnt_d >= 5'd2; + 2'h1: break_err = allzero_cnt_d >= 5'd4; + 2'h2: break_err = allzero_cnt_d >= 5'd8; + default: break_err = allzero_cnt_d >= 5'd16; + endcase + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) break_st_q <= BRK_CHK; + else begin + unique case (break_st_q) + BRK_CHK: begin + if (event_rx_break_err) break_st_q <= BRK_WAIT; + end + + BRK_WAIT: begin + if (rx_in) break_st_q <= BRK_CHK; + end + + // default: begin + // break_st_q <= BRK_CHK; + //end + endcase + end + end + + assign hw2reg.val.d = rx_val_q; + + assign hw2reg.rdata.d = uart_rdata; + + assign hw2reg.status.rxempty.d = ~rx_fifo_rvalid; + assign hw2reg.status.rxidle.d = rx_uart_idle; + assign hw2reg.status.txidle.d = tx_uart_idle & ~tx_fifo_rvalid; + assign hw2reg.status.txempty.d = ~tx_fifo_rvalid; + assign hw2reg.status.rxfull.d = ~rx_fifo_wready; + assign hw2reg.status.txfull.d = ~tx_fifo_wready; + + assign hw2reg.fifo_status.txlvl.d = tx_fifo_depth; + assign hw2reg.fifo_status.rxlvl.d = rx_fifo_depth; + + // resets are self-clearing, so need to update FIFO_CTRL + assign hw2reg.fifo_ctrl.rxilvl.de = 1'b0; + assign hw2reg.fifo_ctrl.rxilvl.d = 3'h0; + assign hw2reg.fifo_ctrl.txilvl.de = 1'b0; + assign hw2reg.fifo_ctrl.txilvl.d = 2'h0; + + // NCO 16x Baud Generator + // output clock rate is: + // Fin * (NCO/2**NcoWidth) + logic [NcoWidth:0] nco_sum_q; // extra bit to get the carry + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + nco_sum_q <= 17'h0; + end else if (tx_enable || rx_enable) begin + nco_sum_q <= {1'b0,nco_sum_q[NcoWidth-1:0]} + {1'b0,reg2hw.ctrl.nco.q[NcoWidth-1:0]}; + end + end + + assign tick_baud_x16 = nco_sum_q[16]; + + ////////////// + // TX Logic // + ////////////// + + assign tx_fifo_rready = tx_uart_idle & tx_fifo_rvalid & tx_enable; + + fifo_sync #( + .Width (8), + .Pass (1'b0), + .Depth (32) + ) u_uart_txfifo ( + .clk_i, + .rst_ni, + .clr_i (uart_fifo_txrst), + .wvalid_i(reg2hw.wdata.qe), + .wready_o(tx_fifo_wready), + .wdata_i (reg2hw.wdata.q), + .depth_o (tx_fifo_depth), + .rvalid_o(tx_fifo_rvalid), + .rready_i(tx_fifo_rready), + .rdata_o (tx_fifo_data) + ); + + uart_tx uart_tx ( + .clk_i, + .rst_ni, + .tx_enable, + .tick_baud_x16, + .parity_enable (reg2hw.ctrl.parity_en.q), + .wr (tx_fifo_rready), + .wr_parity ((^tx_fifo_data) ^ reg2hw.ctrl.parity_odd.q), + .wr_data (tx_fifo_data), + .idle (tx_uart_idle), + .tx (tx_out) + ); + + assign tx = line_loopback ? rx : tx_out_q ; + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + tx_out_q <= 1'b1; + end else if (ovrd_tx_en) begin + tx_out_q <= ovrd_tx_val ; + end else if (sys_loopback) begin + tx_out_q <= 1'b1; + end else begin + tx_out_q <= tx_out; + end + end + + ////////////// + // RX Logic // + ////////////// + + // sync the incoming data + prim_generic_flop_2sync #( + .Width(1), + .ResetValue(1'b1) + ) sync_rx ( + .clk_i, + .rst_ni, + .d_i(rx), + .q_o(rx_sync) + ); + + // Based on: en.wikipedia.org/wiki/Repetition_code mentions the use of a majority filter + // in UART to ignore brief noise spikes + logic rx_sync_q1, rx_sync_q2, rx_in_mx, rx_in_maj; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rx_sync_q1 <= 1'b1; + rx_sync_q2 <= 1'b1; + end else begin + rx_sync_q1 <= rx_sync; + rx_sync_q2 <= rx_sync_q1; + end + end + + assign rx_in_maj = (rx_sync & rx_sync_q1) | + (rx_sync & rx_sync_q2) | + (rx_sync_q1 & rx_sync_q2); + assign rx_in_mx = rxnf_enable ? rx_in_maj : rx_sync; + + assign rx_in = sys_loopback ? tx_out : + line_loopback ? 1'b1 : + rx_in_mx; + + uart_rx uart_rx ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .rx_enable (rx_enable), + .tick_baud_x16 (tick_baud_x16), + .parity_enable (reg2hw.ctrl.parity_en.q), + .parity_odd (reg2hw.ctrl.parity_odd.q), + .tick_baud (rx_tick_baud), + .rx_valid (rx_valid), + .rx_data (rx_fifo_data), + .idle (rx_uart_idle), + .frame_err (event_rx_frame_err), + .rx (rx_in), + .rx_parity_err (event_rx_parity_err) + ); + + assign rx_fifo_wvalid = rx_valid & ~event_rx_frame_err & ~event_rx_parity_err; + + fifo_sync #( + .Width (8), + .Pass (1'b0), + .Depth (32) + ) u_uart_rxfifo ( + .clk_i, + .rst_ni, + .clr_i (uart_fifo_rxrst), + .wvalid_i(rx_fifo_wvalid), + .wready_o(rx_fifo_wready), + .wdata_i (rx_fifo_data), + .depth_o (rx_fifo_depth), + .rvalid_o(rx_fifo_rvalid), + .rready_i(reg2hw.rdata.re), + .rdata_o (uart_rdata) + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) rx_val_q <= 16'h0; + else if (tick_baud_x16) rx_val_q <= {rx_val_q[14:0], rx_in}; + end + + //////////////////////// + // Interrupt & Status // + //////////////////////// + + always_comb begin + unique case(uart_fifo_txilvl) + 2'h0: tx_watermark_d = (tx_fifo_depth < 6'd2); + 2'h1: tx_watermark_d = (tx_fifo_depth < 6'd4); + 2'h2: tx_watermark_d = (tx_fifo_depth < 6'd8); + default: tx_watermark_d = (tx_fifo_depth < 6'd16); + endcase + end + + assign event_tx_watermark = tx_watermark_d & ~tx_watermark_prev_q; + + // The empty condition handling is a bit different. + // If empty rising conditions were detected directly, then every first write of a burst + // would trigger an empty. This is due to the fact that the uart_tx fsm immediately + // withdraws the content and asserts "empty". + // To guard against this false trigger, empty is qualified with idle to extend the window + // in which software has an opportunity to deposit new data. + // However, if software deposit speed is TOO slow, this would still be an issue. + // + // The alternative software fix is to disable tx_enable until it has a chance to + // burst in the desired amount of data. + assign event_tx_empty = ~tx_fifo_rvalid & ~tx_uart_idle_q & tx_uart_idle; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + tx_watermark_prev_q <= 1'b1; // by default watermark condition is true + rx_watermark_prev_q <= 1'b0; // by default watermark condition is false + tx_uart_idle_q <= 1'b1; + end else begin + tx_watermark_prev_q <= tx_watermark_d; + rx_watermark_prev_q <= rx_watermark_d; + tx_uart_idle_q <= tx_uart_idle; + end + end + + always_comb begin + unique case(uart_fifo_rxilvl) + 3'h0: rx_watermark_d = (rx_fifo_depth >= 6'd1); + 3'h1: rx_watermark_d = (rx_fifo_depth >= 6'd4); + 3'h2: rx_watermark_d = (rx_fifo_depth >= 6'd8); + 3'h3: rx_watermark_d = (rx_fifo_depth >= 6'd16); + 3'h4: rx_watermark_d = (rx_fifo_depth >= 6'd30); + default: rx_watermark_d = 1'b0; + endcase + end + + assign event_rx_watermark = rx_watermark_d & ~rx_watermark_prev_q; + + // rx timeout interrupt + assign uart_rxto_en = reg2hw.timeout_ctrl.en.q; + assign uart_rxto_val = reg2hw.timeout_ctrl.val.q; + + assign rx_fifo_depth_changed = (rx_fifo_depth != rx_fifo_depth_prev_q); + + assign rx_timeout_count_d = + // don't count if timeout feature not enabled ; + // will never reach timeout val + lower power + (uart_rxto_en == 1'b0) ? 24'd0 : + // reset count if timeout interrupt is set + event_rx_timeout ? 24'd0 : + // reset count upon change in fifo level: covers both read and receiving a new byte + rx_fifo_depth_changed ? 24'd0 : + // reset count if no bytes are pending + (rx_fifo_depth == 5'd0) ? 24'd0 : + // stop the count at timeout value (this will set the interrupt) + // Removed below line as when the timeout reaches the value, + // event occured, and timeout value reset to 0h. + //(rx_timeout_count_q == uart_rxto_val) ? rx_timeout_count_q : + // increment if at rx baud tick + rx_tick_baud ? (rx_timeout_count_q + 24'd1) : + rx_timeout_count_q; + + assign event_rx_timeout = (rx_timeout_count_q == uart_rxto_val) & uart_rxto_en; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rx_timeout_count_q <= 24'd0; + rx_fifo_depth_prev_q <= 6'd0; + end else begin + rx_timeout_count_q <= rx_timeout_count_d; + rx_fifo_depth_prev_q <= rx_fifo_depth; + end + end + + assign event_rx_overflow = rx_fifo_wvalid & ~rx_fifo_wready; + assign event_rx_break_err = break_err & (break_st_q == BRK_CHK); + + // instantiate interrupt hardware primitives + + prim_intr_hw #(.Width(1)) intr_hw_tx_watermark ( + .clk_i, + .rst_ni, + .event_intr_i (event_tx_watermark), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.tx_watermark.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.tx_watermark.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.tx_watermark.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.tx_watermark.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.tx_watermark.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.tx_watermark.d), + .intr_o (intr_tx_watermark_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_rx_watermark ( + .clk_i, + .rst_ni, + .event_intr_i (event_rx_watermark), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_watermark.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_watermark.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_watermark.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_watermark.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_watermark.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_watermark.d), + .intr_o (intr_rx_watermark_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_tx_empty ( + .clk_i, + .rst_ni, + .event_intr_i (event_tx_empty), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.tx_empty.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.tx_empty.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.tx_empty.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.tx_empty.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.tx_empty.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.tx_empty.d), + .intr_o (intr_tx_empty_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_rx_overflow ( + .clk_i, + .rst_ni, + .event_intr_i (event_rx_overflow), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_overflow.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_overflow.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_overflow.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_overflow.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_overflow.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_overflow.d), + .intr_o (intr_rx_overflow_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_rx_frame_err ( + .clk_i, + .rst_ni, + .event_intr_i (event_rx_frame_err), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_frame_err.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_frame_err.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_frame_err.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_frame_err.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_frame_err.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_frame_err.d), + .intr_o (intr_rx_frame_err_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_rx_break_err ( + .clk_i, + .rst_ni, + .event_intr_i (event_rx_break_err), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_break_err.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_break_err.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_break_err.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_break_err.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_break_err.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_break_err.d), + .intr_o (intr_rx_break_err_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_rx_timeout ( + .clk_i, + .rst_ni, + .event_intr_i (event_rx_timeout), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_timeout.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_timeout.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_timeout.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_timeout.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_timeout.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_timeout.d), + .intr_o (intr_rx_timeout_o) + ); + + prim_intr_hw #(.Width(1)) intr_hw_rx_parity_err ( + .clk_i, + .rst_ni, + .event_intr_i (event_rx_parity_err), + .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_parity_err.q), + .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_parity_err.q), + .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_parity_err.qe), + .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_parity_err.q), + .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_parity_err.de), + .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_parity_err.d), + .intr_o (intr_rx_parity_err_o) + ); + +endmodule
diff --git a/verilog/rtl/uart_reg_pkg.sv b/verilog/rtl/uart_reg_pkg.sv new file mode 100644 index 0000000..22ae7ac --- /dev/null +++ b/verilog/rtl/uart_reg_pkg.sv
@@ -0,0 +1,369 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package uart_reg_pkg; + + // Address width within the block + parameter int BlockAw = 6; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + typedef struct packed { + struct packed { + logic q; + } tx_watermark; + struct packed { + logic q; + } rx_watermark; + struct packed { + logic q; + } tx_empty; + struct packed { + logic q; + } rx_overflow; + struct packed { + logic q; + } rx_frame_err; + struct packed { + logic q; + } rx_break_err; + struct packed { + logic q; + } rx_timeout; + struct packed { + logic q; + } rx_parity_err; + } uart_reg2hw_intr_state_reg_t; + + typedef struct packed { + struct packed { + logic q; + } tx_watermark; + struct packed { + logic q; + } rx_watermark; + struct packed { + logic q; + } tx_empty; + struct packed { + logic q; + } rx_overflow; + struct packed { + logic q; + } rx_frame_err; + struct packed { + logic q; + } rx_break_err; + struct packed { + logic q; + } rx_timeout; + struct packed { + logic q; + } rx_parity_err; + } uart_reg2hw_intr_enable_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } tx_watermark; + struct packed { + logic q; + logic qe; + } rx_watermark; + struct packed { + logic q; + logic qe; + } tx_empty; + struct packed { + logic q; + logic qe; + } rx_overflow; + struct packed { + logic q; + logic qe; + } rx_frame_err; + struct packed { + logic q; + logic qe; + } rx_break_err; + struct packed { + logic q; + logic qe; + } rx_timeout; + struct packed { + logic q; + logic qe; + } rx_parity_err; + } uart_reg2hw_intr_test_reg_t; + + typedef struct packed { + struct packed { + logic q; + } tx; + struct packed { + logic q; + } rx; + struct packed { + logic q; + } nf; + struct packed { + logic q; + } slpbk; + struct packed { + logic q; + } llpbk; + struct packed { + logic q; + } parity_en; + struct packed { + logic q; + } parity_odd; + struct packed { + logic [1:0] q; + } rxblvl; + struct packed { + logic [15:0] q; + } nco; + } uart_reg2hw_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic re; + } txfull; + struct packed { + logic q; + logic re; + } rxfull; + struct packed { + logic q; + logic re; + } txempty; + struct packed { + logic q; + logic re; + } txidle; + struct packed { + logic q; + logic re; + } rxidle; + struct packed { + logic q; + logic re; + } rxempty; + } uart_reg2hw_status_reg_t; + + typedef struct packed { + logic [7:0] q; + logic re; + } uart_reg2hw_rdata_reg_t; + + typedef struct packed { + logic [7:0] q; + logic qe; + } uart_reg2hw_wdata_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic qe; + } rxrst; + struct packed { + logic q; + logic qe; + } txrst; + struct packed { + logic [2:0] q; + logic qe; + } rxilvl; + struct packed { + logic [1:0] q; + logic qe; + } txilvl; + } uart_reg2hw_fifo_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic q; + } txen; + struct packed { + logic q; + } txval; + } uart_reg2hw_ovrd_reg_t; + + typedef struct packed { + struct packed { + logic [23:0] q; + } val; + struct packed { + logic q; + } en; + } uart_reg2hw_timeout_ctrl_reg_t; + + + typedef struct packed { + struct packed { + logic d; + logic de; + } tx_watermark; + struct packed { + logic d; + logic de; + } rx_watermark; + struct packed { + logic d; + logic de; + } tx_empty; + struct packed { + logic d; + logic de; + } rx_overflow; + struct packed { + logic d; + logic de; + } rx_frame_err; + struct packed { + logic d; + logic de; + } rx_break_err; + struct packed { + logic d; + logic de; + } rx_timeout; + struct packed { + logic d; + logic de; + } rx_parity_err; + } uart_hw2reg_intr_state_reg_t; + + typedef struct packed { + struct packed { + logic d; + } txfull; + struct packed { + logic d; + } rxfull; + struct packed { + logic d; + } txempty; + struct packed { + logic d; + } txidle; + struct packed { + logic d; + } rxidle; + struct packed { + logic d; + } rxempty; + } uart_hw2reg_status_reg_t; + + typedef struct packed { + logic [7:0] d; + } uart_hw2reg_rdata_reg_t; + + typedef struct packed { + struct packed { + logic [2:0] d; + logic de; + } rxilvl; + struct packed { + logic [1:0] d; + logic de; + } txilvl; + } uart_hw2reg_fifo_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic [5:0] d; + } txlvl; + struct packed { + logic [5:0] d; + } rxlvl; + } uart_hw2reg_fifo_status_reg_t; + + typedef struct packed { + logic [15:0] d; + } uart_hw2reg_val_reg_t; + + + /////////////////////////////////////// + // Register to internal design logic // + /////////////////////////////////////// + typedef struct packed { + uart_reg2hw_intr_state_reg_t intr_state; // [124:117] + uart_reg2hw_intr_enable_reg_t intr_enable; // [116:109] + uart_reg2hw_intr_test_reg_t intr_test; // [108:93] + uart_reg2hw_ctrl_reg_t ctrl; // [92:68] + uart_reg2hw_status_reg_t status; // [67:56] + uart_reg2hw_rdata_reg_t rdata; // [55:47] + uart_reg2hw_wdata_reg_t wdata; // [46:38] + uart_reg2hw_fifo_ctrl_reg_t fifo_ctrl; // [37:27] + uart_reg2hw_ovrd_reg_t ovrd; // [26:25] + uart_reg2hw_timeout_ctrl_reg_t timeout_ctrl; // [24:0] + } uart_reg2hw_t; + + /////////////////////////////////////// + // Internal design logic to register // + /////////////////////////////////////// + typedef struct packed { + uart_hw2reg_intr_state_reg_t intr_state; // [64:49] + uart_hw2reg_status_reg_t status; // [48:43] + uart_hw2reg_rdata_reg_t rdata; // [42:35] + uart_hw2reg_fifo_ctrl_reg_t fifo_ctrl; // [34:28] + uart_hw2reg_fifo_status_reg_t fifo_status; // [27:16] + uart_hw2reg_val_reg_t val; // [15:0] + } uart_hw2reg_t; + + // Register Address + parameter logic [BlockAw-1:0] UART_INTR_STATE_OFFSET = 6'h 0; + parameter logic [BlockAw-1:0] UART_INTR_ENABLE_OFFSET = 6'h 4; + parameter logic [BlockAw-1:0] UART_INTR_TEST_OFFSET = 6'h 8; + parameter logic [BlockAw-1:0] UART_CTRL_OFFSET = 6'h c; + parameter logic [BlockAw-1:0] UART_STATUS_OFFSET = 6'h 10; + parameter logic [BlockAw-1:0] UART_RDATA_OFFSET = 6'h 14; + parameter logic [BlockAw-1:0] UART_WDATA_OFFSET = 6'h 18; + parameter logic [BlockAw-1:0] UART_FIFO_CTRL_OFFSET = 6'h 1c; + parameter logic [BlockAw-1:0] UART_FIFO_STATUS_OFFSET = 6'h 20; + parameter logic [BlockAw-1:0] UART_OVRD_OFFSET = 6'h 24; + parameter logic [BlockAw-1:0] UART_VAL_OFFSET = 6'h 28; + parameter logic [BlockAw-1:0] UART_TIMEOUT_CTRL_OFFSET = 6'h 2c; + + + // Register Index + typedef enum int { + UART_INTR_STATE, + UART_INTR_ENABLE, + UART_INTR_TEST, + UART_CTRL, + UART_STATUS, + UART_RDATA, + UART_WDATA, + UART_FIFO_CTRL, + UART_FIFO_STATUS, + UART_OVRD, + UART_VAL, + UART_TIMEOUT_CTRL + } uart_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] UART_PERMIT [12] = '{ + 4'b 0001, // index[ 0] UART_INTR_STATE + 4'b 0001, // index[ 1] UART_INTR_ENABLE + 4'b 0001, // index[ 2] UART_INTR_TEST + 4'b 1111, // index[ 3] UART_CTRL + 4'b 0001, // index[ 4] UART_STATUS + 4'b 0001, // index[ 5] UART_RDATA + 4'b 0001, // index[ 6] UART_WDATA + 4'b 0001, // index[ 7] UART_FIFO_CTRL + 4'b 0111, // index[ 8] UART_FIFO_STATUS + 4'b 0001, // index[ 9] UART_OVRD + 4'b 0011, // index[10] UART_VAL + 4'b 1111 // index[11] UART_TIMEOUT_CTRL + }; +endpackage +
diff --git a/verilog/rtl/uart_reg_top.sv b/verilog/rtl/uart_reg_top.sv new file mode 100644 index 0000000..4342e2a --- /dev/null +++ b/verilog/rtl/uart_reg_top.sv
@@ -0,0 +1,1677 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + +// `include "prim_assert.sv" + +module uart_reg_top ( + input clk_i, + input rst_ni, + + // Below Regster interface can be changed + input tlul_pkg::tl_h2d_t tl_i, + output tlul_pkg::tl_d2h_t tl_o, + // To HW + output uart_reg_pkg::uart_reg2hw_t reg2hw, // Write + input uart_reg_pkg::uart_hw2reg_t hw2reg, // Read + + // Config + input devmode_i // If 1, explicit error return for unmapped register access +); + + import uart_reg_pkg::* ; + + localparam int AW = 6; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + // register signals + logic reg_we; + logic reg_re; + logic [AW-1:0] reg_addr; + logic [DW-1:0] reg_wdata; + logic [DBW-1:0] reg_be; + logic [DW-1:0] reg_rdata; + logic reg_error; + + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + tlul_pkg::tl_h2d_t tl_reg_h2d; + tlul_pkg::tl_d2h_t tl_reg_d2h; + + assign tl_reg_h2d = tl_i; + assign tl_o = tl_reg_d2h; + + tlul_adapter_reg #( + .RegAw(AW), + .RegDw(DW) + ) u_reg_if ( + .clk_i, + .rst_ni, + + .tl_i (tl_reg_h2d), + .tl_o (tl_reg_d2h), + + .we_o (reg_we), + .re_o (reg_re), + .addr_o (reg_addr), + .wdata_o (reg_wdata), + .be_o (reg_be), + .rdata_i (reg_rdata), + .error_i (reg_error) + ); + + assign reg_rdata = reg_rdata_next ; + assign reg_error = (devmode_i & addrmiss) | wr_err ; + + // Define SW related signals + // Format: <reg>_<field>_{wd|we|qs} + // or <reg>_{wd|we|qs} if field == 1 or 0 + logic intr_state_tx_watermark_qs; + logic intr_state_tx_watermark_wd; + logic intr_state_tx_watermark_we; + logic intr_state_rx_watermark_qs; + logic intr_state_rx_watermark_wd; + logic intr_state_rx_watermark_we; + logic intr_state_tx_empty_qs; + logic intr_state_tx_empty_wd; + logic intr_state_tx_empty_we; + logic intr_state_rx_overflow_qs; + logic intr_state_rx_overflow_wd; + logic intr_state_rx_overflow_we; + logic intr_state_rx_frame_err_qs; + logic intr_state_rx_frame_err_wd; + logic intr_state_rx_frame_err_we; + logic intr_state_rx_break_err_qs; + logic intr_state_rx_break_err_wd; + logic intr_state_rx_break_err_we; + logic intr_state_rx_timeout_qs; + logic intr_state_rx_timeout_wd; + logic intr_state_rx_timeout_we; + logic intr_state_rx_parity_err_qs; + logic intr_state_rx_parity_err_wd; + logic intr_state_rx_parity_err_we; + logic intr_enable_tx_watermark_qs; + logic intr_enable_tx_watermark_wd; + logic intr_enable_tx_watermark_we; + logic intr_enable_rx_watermark_qs; + logic intr_enable_rx_watermark_wd; + logic intr_enable_rx_watermark_we; + logic intr_enable_tx_empty_qs; + logic intr_enable_tx_empty_wd; + logic intr_enable_tx_empty_we; + logic intr_enable_rx_overflow_qs; + logic intr_enable_rx_overflow_wd; + logic intr_enable_rx_overflow_we; + logic intr_enable_rx_frame_err_qs; + logic intr_enable_rx_frame_err_wd; + logic intr_enable_rx_frame_err_we; + logic intr_enable_rx_break_err_qs; + logic intr_enable_rx_break_err_wd; + logic intr_enable_rx_break_err_we; + logic intr_enable_rx_timeout_qs; + logic intr_enable_rx_timeout_wd; + logic intr_enable_rx_timeout_we; + logic intr_enable_rx_parity_err_qs; + logic intr_enable_rx_parity_err_wd; + logic intr_enable_rx_parity_err_we; + logic intr_test_tx_watermark_wd; + logic intr_test_tx_watermark_we; + logic intr_test_rx_watermark_wd; + logic intr_test_rx_watermark_we; + logic intr_test_tx_empty_wd; + logic intr_test_tx_empty_we; + logic intr_test_rx_overflow_wd; + logic intr_test_rx_overflow_we; + logic intr_test_rx_frame_err_wd; + logic intr_test_rx_frame_err_we; + logic intr_test_rx_break_err_wd; + logic intr_test_rx_break_err_we; + logic intr_test_rx_timeout_wd; + logic intr_test_rx_timeout_we; + logic intr_test_rx_parity_err_wd; + logic intr_test_rx_parity_err_we; + logic ctrl_tx_qs; + logic ctrl_tx_wd; + logic ctrl_tx_we; + logic ctrl_rx_qs; + logic ctrl_rx_wd; + logic ctrl_rx_we; + logic ctrl_nf_qs; + logic ctrl_nf_wd; + logic ctrl_nf_we; + logic ctrl_slpbk_qs; + logic ctrl_slpbk_wd; + logic ctrl_slpbk_we; + logic ctrl_llpbk_qs; + logic ctrl_llpbk_wd; + logic ctrl_llpbk_we; + logic ctrl_parity_en_qs; + logic ctrl_parity_en_wd; + logic ctrl_parity_en_we; + logic ctrl_parity_odd_qs; + logic ctrl_parity_odd_wd; + logic ctrl_parity_odd_we; + logic [1:0] ctrl_rxblvl_qs; + logic [1:0] ctrl_rxblvl_wd; + logic ctrl_rxblvl_we; + logic [15:0] ctrl_nco_qs; + logic [15:0] ctrl_nco_wd; + logic ctrl_nco_we; + logic status_txfull_qs; + logic status_txfull_re; + logic status_rxfull_qs; + logic status_rxfull_re; + logic status_txempty_qs; + logic status_txempty_re; + logic status_txidle_qs; + logic status_txidle_re; + logic status_rxidle_qs; + logic status_rxidle_re; + logic status_rxempty_qs; + logic status_rxempty_re; + logic [7:0] rdata_qs; + logic rdata_re; + logic [7:0] wdata_wd; + logic wdata_we; + logic fifo_ctrl_rxrst_wd; + logic fifo_ctrl_rxrst_we; + logic fifo_ctrl_txrst_wd; + logic fifo_ctrl_txrst_we; + logic [2:0] fifo_ctrl_rxilvl_qs; + logic [2:0] fifo_ctrl_rxilvl_wd; + logic fifo_ctrl_rxilvl_we; + logic [1:0] fifo_ctrl_txilvl_qs; + logic [1:0] fifo_ctrl_txilvl_wd; + logic fifo_ctrl_txilvl_we; + logic [5:0] fifo_status_txlvl_qs; + logic fifo_status_txlvl_re; + logic [5:0] fifo_status_rxlvl_qs; + logic fifo_status_rxlvl_re; + logic ovrd_txen_qs; + logic ovrd_txen_wd; + logic ovrd_txen_we; + logic ovrd_txval_qs; + logic ovrd_txval_wd; + logic ovrd_txval_we; + logic [15:0] val_qs; + logic val_re; + logic [23:0] timeout_ctrl_val_qs; + logic [23:0] timeout_ctrl_val_wd; + logic timeout_ctrl_val_we; + logic timeout_ctrl_en_qs; + logic timeout_ctrl_en_wd; + logic timeout_ctrl_en_we; + + // Register instances + // R[intr_state]: V(False) + + // F[tx_watermark]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_tx_watermark ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_tx_watermark_we), + .wd (intr_state_tx_watermark_wd), + + // from internal hardware + .de (hw2reg.intr_state.tx_watermark.de), + .d (hw2reg.intr_state.tx_watermark.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.tx_watermark.q ), + + // to register interface (read) + .qs (intr_state_tx_watermark_qs) + ); + + + // F[rx_watermark]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_rx_watermark ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_rx_watermark_we), + .wd (intr_state_rx_watermark_wd), + + // from internal hardware + .de (hw2reg.intr_state.rx_watermark.de), + .d (hw2reg.intr_state.rx_watermark.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.rx_watermark.q ), + + // to register interface (read) + .qs (intr_state_rx_watermark_qs) + ); + + + // F[tx_empty]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_tx_empty ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_tx_empty_we), + .wd (intr_state_tx_empty_wd), + + // from internal hardware + .de (hw2reg.intr_state.tx_empty.de), + .d (hw2reg.intr_state.tx_empty.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.tx_empty.q ), + + // to register interface (read) + .qs (intr_state_tx_empty_qs) + ); + + + // F[rx_overflow]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_rx_overflow ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_rx_overflow_we), + .wd (intr_state_rx_overflow_wd), + + // from internal hardware + .de (hw2reg.intr_state.rx_overflow.de), + .d (hw2reg.intr_state.rx_overflow.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.rx_overflow.q ), + + // to register interface (read) + .qs (intr_state_rx_overflow_qs) + ); + + + // F[rx_frame_err]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_rx_frame_err ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_rx_frame_err_we), + .wd (intr_state_rx_frame_err_wd), + + // from internal hardware + .de (hw2reg.intr_state.rx_frame_err.de), + .d (hw2reg.intr_state.rx_frame_err.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.rx_frame_err.q ), + + // to register interface (read) + .qs (intr_state_rx_frame_err_qs) + ); + + + // F[rx_break_err]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_rx_break_err ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_rx_break_err_we), + .wd (intr_state_rx_break_err_wd), + + // from internal hardware + .de (hw2reg.intr_state.rx_break_err.de), + .d (hw2reg.intr_state.rx_break_err.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.rx_break_err.q ), + + // to register interface (read) + .qs (intr_state_rx_break_err_qs) + ); + + + // F[rx_timeout]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_rx_timeout ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_rx_timeout_we), + .wd (intr_state_rx_timeout_wd), + + // from internal hardware + .de (hw2reg.intr_state.rx_timeout.de), + .d (hw2reg.intr_state.rx_timeout.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.rx_timeout.q ), + + // to register interface (read) + .qs (intr_state_rx_timeout_qs) + ); + + + // F[rx_parity_err]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_intr_state_rx_parity_err ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_state_rx_parity_err_we), + .wd (intr_state_rx_parity_err_wd), + + // from internal hardware + .de (hw2reg.intr_state.rx_parity_err.de), + .d (hw2reg.intr_state.rx_parity_err.d ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_state.rx_parity_err.q ), + + // to register interface (read) + .qs (intr_state_rx_parity_err_qs) + ); + + + // R[intr_enable]: V(False) + + // F[tx_watermark]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_tx_watermark ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_tx_watermark_we), + .wd (intr_enable_tx_watermark_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.tx_watermark.q ), + + // to register interface (read) + .qs (intr_enable_tx_watermark_qs) + ); + + + // F[rx_watermark]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_rx_watermark ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_rx_watermark_we), + .wd (intr_enable_rx_watermark_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.rx_watermark.q ), + + // to register interface (read) + .qs (intr_enable_rx_watermark_qs) + ); + + + // F[tx_empty]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_tx_empty ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_tx_empty_we), + .wd (intr_enable_tx_empty_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.tx_empty.q ), + + // to register interface (read) + .qs (intr_enable_tx_empty_qs) + ); + + + // F[rx_overflow]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_rx_overflow ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_rx_overflow_we), + .wd (intr_enable_rx_overflow_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.rx_overflow.q ), + + // to register interface (read) + .qs (intr_enable_rx_overflow_qs) + ); + + + // F[rx_frame_err]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_rx_frame_err ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_rx_frame_err_we), + .wd (intr_enable_rx_frame_err_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.rx_frame_err.q ), + + // to register interface (read) + .qs (intr_enable_rx_frame_err_qs) + ); + + + // F[rx_break_err]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_rx_break_err ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_rx_break_err_we), + .wd (intr_enable_rx_break_err_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.rx_break_err.q ), + + // to register interface (read) + .qs (intr_enable_rx_break_err_qs) + ); + + + // F[rx_timeout]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_rx_timeout ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_rx_timeout_we), + .wd (intr_enable_rx_timeout_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.rx_timeout.q ), + + // to register interface (read) + .qs (intr_enable_rx_timeout_qs) + ); + + + // F[rx_parity_err]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_intr_enable_rx_parity_err ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (intr_enable_rx_parity_err_we), + .wd (intr_enable_rx_parity_err_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.intr_enable.rx_parity_err.q ), + + // to register interface (read) + .qs (intr_enable_rx_parity_err_qs) + ); + + + // R[intr_test]: V(True) + + // F[tx_watermark]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_tx_watermark ( + .re (1'b0), + .we (intr_test_tx_watermark_we), + .wd (intr_test_tx_watermark_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.tx_watermark.qe), + .q (reg2hw.intr_test.tx_watermark.q ), + .qs () + ); + + + // F[rx_watermark]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_rx_watermark ( + .re (1'b0), + .we (intr_test_rx_watermark_we), + .wd (intr_test_rx_watermark_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.rx_watermark.qe), + .q (reg2hw.intr_test.rx_watermark.q ), + .qs () + ); + + + // F[tx_empty]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_tx_empty ( + .re (1'b0), + .we (intr_test_tx_empty_we), + .wd (intr_test_tx_empty_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.tx_empty.qe), + .q (reg2hw.intr_test.tx_empty.q ), + .qs () + ); + + + // F[rx_overflow]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_rx_overflow ( + .re (1'b0), + .we (intr_test_rx_overflow_we), + .wd (intr_test_rx_overflow_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.rx_overflow.qe), + .q (reg2hw.intr_test.rx_overflow.q ), + .qs () + ); + + + // F[rx_frame_err]: 4:4 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_rx_frame_err ( + .re (1'b0), + .we (intr_test_rx_frame_err_we), + .wd (intr_test_rx_frame_err_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.rx_frame_err.qe), + .q (reg2hw.intr_test.rx_frame_err.q ), + .qs () + ); + + + // F[rx_break_err]: 5:5 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_rx_break_err ( + .re (1'b0), + .we (intr_test_rx_break_err_we), + .wd (intr_test_rx_break_err_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.rx_break_err.qe), + .q (reg2hw.intr_test.rx_break_err.q ), + .qs () + ); + + + // F[rx_timeout]: 6:6 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_rx_timeout ( + .re (1'b0), + .we (intr_test_rx_timeout_we), + .wd (intr_test_rx_timeout_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.rx_timeout.qe), + .q (reg2hw.intr_test.rx_timeout.q ), + .qs () + ); + + + // F[rx_parity_err]: 7:7 + prim_subreg_ext #( + .DW (1) + ) u_intr_test_rx_parity_err ( + .re (1'b0), + .we (intr_test_rx_parity_err_we), + .wd (intr_test_rx_parity_err_wd), + .d ('0), + .qre (), + .qe (reg2hw.intr_test.rx_parity_err.qe), + .q (reg2hw.intr_test.rx_parity_err.q ), + .qs () + ); + + + // R[ctrl]: V(False) + + // F[tx]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_tx ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_tx_we), + .wd (ctrl_tx_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.tx.q ), + + // to register interface (read) + .qs (ctrl_tx_qs) + ); + + + // F[rx]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_rx ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_rx_we), + .wd (ctrl_rx_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.rx.q ), + + // to register interface (read) + .qs (ctrl_rx_qs) + ); + + + // F[nf]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_nf ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_nf_we), + .wd (ctrl_nf_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.nf.q ), + + // to register interface (read) + .qs (ctrl_nf_qs) + ); + + + // F[slpbk]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_slpbk ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_slpbk_we), + .wd (ctrl_slpbk_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.slpbk.q ), + + // to register interface (read) + .qs (ctrl_slpbk_qs) + ); + + + // F[llpbk]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_llpbk ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_llpbk_we), + .wd (ctrl_llpbk_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.llpbk.q ), + + // to register interface (read) + .qs (ctrl_llpbk_qs) + ); + + + // F[parity_en]: 6:6 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_parity_en ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_parity_en_we), + .wd (ctrl_parity_en_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.parity_en.q ), + + // to register interface (read) + .qs (ctrl_parity_en_qs) + ); + + + // F[parity_odd]: 7:7 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_parity_odd ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_parity_odd_we), + .wd (ctrl_parity_odd_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.parity_odd.q ), + + // to register interface (read) + .qs (ctrl_parity_odd_qs) + ); + + + // F[rxblvl]: 9:8 + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_ctrl_rxblvl ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_rxblvl_we), + .wd (ctrl_rxblvl_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.rxblvl.q ), + + // to register interface (read) + .qs (ctrl_rxblvl_qs) + ); + + + // F[nco]: 31:16 + prim_subreg #( + .DW (16), + .SWACCESS("RW"), + .RESVAL (16'h0) + ) u_ctrl_nco ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ctrl_nco_we), + .wd (ctrl_nco_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ctrl.nco.q ), + + // to register interface (read) + .qs (ctrl_nco_qs) + ); + + + // R[status]: V(True) + + // F[txfull]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_status_txfull ( + .re (status_txfull_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.txfull.d), + .qre (reg2hw.status.txfull.re), + .qe (), + .q (reg2hw.status.txfull.q ), + .qs (status_txfull_qs) + ); + + + // F[rxfull]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_status_rxfull ( + .re (status_rxfull_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.rxfull.d), + .qre (reg2hw.status.rxfull.re), + .qe (), + .q (reg2hw.status.rxfull.q ), + .qs (status_rxfull_qs) + ); + + + // F[txempty]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_status_txempty ( + .re (status_txempty_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.txempty.d), + .qre (reg2hw.status.txempty.re), + .qe (), + .q (reg2hw.status.txempty.q ), + .qs (status_txempty_qs) + ); + + + // F[txidle]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_status_txidle ( + .re (status_txidle_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.txidle.d), + .qre (reg2hw.status.txidle.re), + .qe (), + .q (reg2hw.status.txidle.q ), + .qs (status_txidle_qs) + ); + + + // F[rxidle]: 4:4 + prim_subreg_ext #( + .DW (1) + ) u_status_rxidle ( + .re (status_rxidle_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.rxidle.d), + .qre (reg2hw.status.rxidle.re), + .qe (), + .q (reg2hw.status.rxidle.q ), + .qs (status_rxidle_qs) + ); + + + // F[rxempty]: 5:5 + prim_subreg_ext #( + .DW (1) + ) u_status_rxempty ( + .re (status_rxempty_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.rxempty.d), + .qre (reg2hw.status.rxempty.re), + .qe (), + .q (reg2hw.status.rxempty.q ), + .qs (status_rxempty_qs) + ); + + + // R[rdata]: V(True) + + prim_subreg_ext #( + .DW (8) + ) u_rdata ( + .re (rdata_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.rdata.d), + .qre (reg2hw.rdata.re), + .qe (), + .q (reg2hw.rdata.q ), + .qs (rdata_qs) + ); + + + // R[wdata]: V(False) + + prim_subreg #( + .DW (8), + .SWACCESS("WO"), + .RESVAL (8'h0) + ) u_wdata ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (wdata_we), + .wd (wdata_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (reg2hw.wdata.qe), + .q (reg2hw.wdata.q ), + + .qs () + ); + + + // R[fifo_ctrl]: V(False) + + // F[rxrst]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("WO"), + .RESVAL (1'h0) + ) u_fifo_ctrl_rxrst ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (fifo_ctrl_rxrst_we), + .wd (fifo_ctrl_rxrst_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (reg2hw.fifo_ctrl.rxrst.qe), + .q (reg2hw.fifo_ctrl.rxrst.q ), + + .qs () + ); + + + // F[txrst]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("WO"), + .RESVAL (1'h0) + ) u_fifo_ctrl_txrst ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (fifo_ctrl_txrst_we), + .wd (fifo_ctrl_txrst_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (reg2hw.fifo_ctrl.txrst.qe), + .q (reg2hw.fifo_ctrl.txrst.q ), + + .qs () + ); + + + // F[rxilvl]: 4:2 + prim_subreg #( + .DW (3), + .SWACCESS("RW"), + .RESVAL (3'h0) + ) u_fifo_ctrl_rxilvl ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (fifo_ctrl_rxilvl_we), + .wd (fifo_ctrl_rxilvl_wd), + + // from internal hardware + .de (hw2reg.fifo_ctrl.rxilvl.de), + .d (hw2reg.fifo_ctrl.rxilvl.d ), + + // to internal hardware + .qe (reg2hw.fifo_ctrl.rxilvl.qe), + .q (reg2hw.fifo_ctrl.rxilvl.q ), + + // to register interface (read) + .qs (fifo_ctrl_rxilvl_qs) + ); + + + // F[txilvl]: 6:5 + prim_subreg #( + .DW (2), + .SWACCESS("RW"), + .RESVAL (2'h0) + ) u_fifo_ctrl_txilvl ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (fifo_ctrl_txilvl_we), + .wd (fifo_ctrl_txilvl_wd), + + // from internal hardware + .de (hw2reg.fifo_ctrl.txilvl.de), + .d (hw2reg.fifo_ctrl.txilvl.d ), + + // to internal hardware + .qe (reg2hw.fifo_ctrl.txilvl.qe), + .q (reg2hw.fifo_ctrl.txilvl.q ), + + // to register interface (read) + .qs (fifo_ctrl_txilvl_qs) + ); + + + // R[fifo_status]: V(True) + + // F[txlvl]: 5:0 + prim_subreg_ext #( + .DW (6) + ) u_fifo_status_txlvl ( + .re (fifo_status_txlvl_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.fifo_status.txlvl.d), + .qre (), + .qe (), + .q (), + .qs (fifo_status_txlvl_qs) + ); + + + // F[rxlvl]: 21:16 + prim_subreg_ext #( + .DW (6) + ) u_fifo_status_rxlvl ( + .re (fifo_status_rxlvl_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.fifo_status.rxlvl.d), + .qre (), + .qe (), + .q (), + .qs (fifo_status_rxlvl_qs) + ); + + + // R[ovrd]: V(False) + + // F[txen]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ovrd_txen ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ovrd_txen_we), + .wd (ovrd_txen_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ovrd.txen.q ), + + // to register interface (read) + .qs (ovrd_txen_qs) + ); + + + // F[txval]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ovrd_txval ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (ovrd_txval_we), + .wd (ovrd_txval_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.ovrd.txval.q ), + + // to register interface (read) + .qs (ovrd_txval_qs) + ); + + + // R[val]: V(True) + + prim_subreg_ext #( + .DW (16) + ) u_val ( + .re (val_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.val.d), + .qre (), + .qe (), + .q (), + .qs (val_qs) + ); + + + // R[timeout_ctrl]: V(False) + + // F[val]: 23:0 + prim_subreg #( + .DW (24), + .SWACCESS("RW"), + .RESVAL (24'h0) + ) u_timeout_ctrl_val ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (timeout_ctrl_val_we), + .wd (timeout_ctrl_val_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.timeout_ctrl.val.q ), + + // to register interface (read) + .qs (timeout_ctrl_val_qs) + ); + + + // F[en]: 31:31 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_timeout_ctrl_en ( + .clk_i (clk_i ), + .rst_ni (rst_ni ), + + // from register interface + .we (timeout_ctrl_en_we), + .wd (timeout_ctrl_en_wd), + + // from internal hardware + .de (1'b0), + .d ('0 ), + + // to internal hardware + .qe (), + .q (reg2hw.timeout_ctrl.en.q ), + + // to register interface (read) + .qs (timeout_ctrl_en_qs) + ); + + + + + logic [11:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[ 0] = (reg_addr == UART_INTR_STATE_OFFSET); + addr_hit[ 1] = (reg_addr == UART_INTR_ENABLE_OFFSET); + addr_hit[ 2] = (reg_addr == UART_INTR_TEST_OFFSET); + addr_hit[ 3] = (reg_addr == UART_CTRL_OFFSET); + addr_hit[ 4] = (reg_addr == UART_STATUS_OFFSET); + addr_hit[ 5] = (reg_addr == UART_RDATA_OFFSET); + addr_hit[ 6] = (reg_addr == UART_WDATA_OFFSET); + addr_hit[ 7] = (reg_addr == UART_FIFO_CTRL_OFFSET); + addr_hit[ 8] = (reg_addr == UART_FIFO_STATUS_OFFSET); + addr_hit[ 9] = (reg_addr == UART_OVRD_OFFSET); + addr_hit[10] = (reg_addr == UART_VAL_OFFSET); + addr_hit[11] = (reg_addr == UART_TIMEOUT_CTRL_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = 1'b0; + if (addr_hit[ 0] && reg_we && (UART_PERMIT[ 0] != (UART_PERMIT[ 0] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 1] && reg_we && (UART_PERMIT[ 1] != (UART_PERMIT[ 1] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 2] && reg_we && (UART_PERMIT[ 2] != (UART_PERMIT[ 2] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 3] && reg_we && (UART_PERMIT[ 3] != (UART_PERMIT[ 3] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 4] && reg_we && (UART_PERMIT[ 4] != (UART_PERMIT[ 4] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 5] && reg_we && (UART_PERMIT[ 5] != (UART_PERMIT[ 5] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 6] && reg_we && (UART_PERMIT[ 6] != (UART_PERMIT[ 6] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 7] && reg_we && (UART_PERMIT[ 7] != (UART_PERMIT[ 7] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 8] && reg_we && (UART_PERMIT[ 8] != (UART_PERMIT[ 8] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[ 9] && reg_we && (UART_PERMIT[ 9] != (UART_PERMIT[ 9] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[10] && reg_we && (UART_PERMIT[10] != (UART_PERMIT[10] & reg_be))) wr_err = 1'b1 ; + if (addr_hit[11] && reg_we && (UART_PERMIT[11] != (UART_PERMIT[11] & reg_be))) wr_err = 1'b1 ; + end + + assign intr_state_tx_watermark_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_tx_watermark_wd = reg_wdata[0]; + + assign intr_state_rx_watermark_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_rx_watermark_wd = reg_wdata[1]; + + assign intr_state_tx_empty_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_tx_empty_wd = reg_wdata[2]; + + assign intr_state_rx_overflow_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_rx_overflow_wd = reg_wdata[3]; + + assign intr_state_rx_frame_err_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_rx_frame_err_wd = reg_wdata[4]; + + assign intr_state_rx_break_err_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_rx_break_err_wd = reg_wdata[5]; + + assign intr_state_rx_timeout_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_rx_timeout_wd = reg_wdata[6]; + + assign intr_state_rx_parity_err_we = addr_hit[0] & reg_we & ~wr_err; + assign intr_state_rx_parity_err_wd = reg_wdata[7]; + + assign intr_enable_tx_watermark_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_tx_watermark_wd = reg_wdata[0]; + + assign intr_enable_rx_watermark_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_rx_watermark_wd = reg_wdata[1]; + + assign intr_enable_tx_empty_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_tx_empty_wd = reg_wdata[2]; + + assign intr_enable_rx_overflow_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_rx_overflow_wd = reg_wdata[3]; + + assign intr_enable_rx_frame_err_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_rx_frame_err_wd = reg_wdata[4]; + + assign intr_enable_rx_break_err_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_rx_break_err_wd = reg_wdata[5]; + + assign intr_enable_rx_timeout_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_rx_timeout_wd = reg_wdata[6]; + + assign intr_enable_rx_parity_err_we = addr_hit[1] & reg_we & ~wr_err; + assign intr_enable_rx_parity_err_wd = reg_wdata[7]; + + assign intr_test_tx_watermark_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_tx_watermark_wd = reg_wdata[0]; + + assign intr_test_rx_watermark_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_rx_watermark_wd = reg_wdata[1]; + + assign intr_test_tx_empty_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_tx_empty_wd = reg_wdata[2]; + + assign intr_test_rx_overflow_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_rx_overflow_wd = reg_wdata[3]; + + assign intr_test_rx_frame_err_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_rx_frame_err_wd = reg_wdata[4]; + + assign intr_test_rx_break_err_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_rx_break_err_wd = reg_wdata[5]; + + assign intr_test_rx_timeout_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_rx_timeout_wd = reg_wdata[6]; + + assign intr_test_rx_parity_err_we = addr_hit[2] & reg_we & ~wr_err; + assign intr_test_rx_parity_err_wd = reg_wdata[7]; + + assign ctrl_tx_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_tx_wd = reg_wdata[0]; + + assign ctrl_rx_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_rx_wd = reg_wdata[1]; + + assign ctrl_nf_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_nf_wd = reg_wdata[2]; + + assign ctrl_slpbk_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_slpbk_wd = reg_wdata[4]; + + assign ctrl_llpbk_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_llpbk_wd = reg_wdata[5]; + + assign ctrl_parity_en_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_parity_en_wd = reg_wdata[6]; + + assign ctrl_parity_odd_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_parity_odd_wd = reg_wdata[7]; + + assign ctrl_rxblvl_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_rxblvl_wd = reg_wdata[9:8]; + + assign ctrl_nco_we = addr_hit[3] & reg_we & ~wr_err; + assign ctrl_nco_wd = reg_wdata[31:16]; + + assign status_txfull_re = addr_hit[4] && reg_re; + + assign status_rxfull_re = addr_hit[4] && reg_re; + + assign status_txempty_re = addr_hit[4] && reg_re; + + assign status_txidle_re = addr_hit[4] && reg_re; + + assign status_rxidle_re = addr_hit[4] && reg_re; + + assign status_rxempty_re = addr_hit[4] && reg_re; + + assign rdata_re = addr_hit[5] && reg_re; + + assign wdata_we = addr_hit[6] & reg_we & ~wr_err; + assign wdata_wd = reg_wdata[7:0]; + + assign fifo_ctrl_rxrst_we = addr_hit[7] & reg_we & ~wr_err; + assign fifo_ctrl_rxrst_wd = reg_wdata[0]; + + assign fifo_ctrl_txrst_we = addr_hit[7] & reg_we & ~wr_err; + assign fifo_ctrl_txrst_wd = reg_wdata[1]; + + assign fifo_ctrl_rxilvl_we = addr_hit[7] & reg_we & ~wr_err; + assign fifo_ctrl_rxilvl_wd = reg_wdata[4:2]; + + assign fifo_ctrl_txilvl_we = addr_hit[7] & reg_we & ~wr_err; + assign fifo_ctrl_txilvl_wd = reg_wdata[6:5]; + + assign fifo_status_txlvl_re = addr_hit[8] && reg_re; + + assign fifo_status_rxlvl_re = addr_hit[8] && reg_re; + + assign ovrd_txen_we = addr_hit[9] & reg_we & ~wr_err; + assign ovrd_txen_wd = reg_wdata[0]; + + assign ovrd_txval_we = addr_hit[9] & reg_we & ~wr_err; + assign ovrd_txval_wd = reg_wdata[1]; + + assign val_re = addr_hit[10] && reg_re; + + assign timeout_ctrl_val_we = addr_hit[11] & reg_we & ~wr_err; + assign timeout_ctrl_val_wd = reg_wdata[23:0]; + + assign timeout_ctrl_en_we = addr_hit[11] & reg_we & ~wr_err; + assign timeout_ctrl_en_wd = reg_wdata[31]; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = intr_state_tx_watermark_qs; + reg_rdata_next[1] = intr_state_rx_watermark_qs; + reg_rdata_next[2] = intr_state_tx_empty_qs; + reg_rdata_next[3] = intr_state_rx_overflow_qs; + reg_rdata_next[4] = intr_state_rx_frame_err_qs; + reg_rdata_next[5] = intr_state_rx_break_err_qs; + reg_rdata_next[6] = intr_state_rx_timeout_qs; + reg_rdata_next[7] = intr_state_rx_parity_err_qs; + end + + addr_hit[1]: begin + reg_rdata_next[0] = intr_enable_tx_watermark_qs; + reg_rdata_next[1] = intr_enable_rx_watermark_qs; + reg_rdata_next[2] = intr_enable_tx_empty_qs; + reg_rdata_next[3] = intr_enable_rx_overflow_qs; + reg_rdata_next[4] = intr_enable_rx_frame_err_qs; + reg_rdata_next[5] = intr_enable_rx_break_err_qs; + reg_rdata_next[6] = intr_enable_rx_timeout_qs; + reg_rdata_next[7] = intr_enable_rx_parity_err_qs; + end + + addr_hit[2]: begin + reg_rdata_next[0] = '0; + reg_rdata_next[1] = '0; + reg_rdata_next[2] = '0; + reg_rdata_next[3] = '0; + reg_rdata_next[4] = '0; + reg_rdata_next[5] = '0; + reg_rdata_next[6] = '0; + reg_rdata_next[7] = '0; + end + + addr_hit[3]: begin + reg_rdata_next[0] = ctrl_tx_qs; + reg_rdata_next[1] = ctrl_rx_qs; + reg_rdata_next[2] = ctrl_nf_qs; + reg_rdata_next[4] = ctrl_slpbk_qs; + reg_rdata_next[5] = ctrl_llpbk_qs; + reg_rdata_next[6] = ctrl_parity_en_qs; + reg_rdata_next[7] = ctrl_parity_odd_qs; + reg_rdata_next[9:8] = ctrl_rxblvl_qs; + reg_rdata_next[31:16] = ctrl_nco_qs; + end + + addr_hit[4]: begin + reg_rdata_next[0] = status_txfull_qs; + reg_rdata_next[1] = status_rxfull_qs; + reg_rdata_next[2] = status_txempty_qs; + reg_rdata_next[3] = status_txidle_qs; + reg_rdata_next[4] = status_rxidle_qs; + reg_rdata_next[5] = status_rxempty_qs; + end + + addr_hit[5]: begin + reg_rdata_next[7:0] = rdata_qs; + end + + addr_hit[6]: begin + reg_rdata_next[7:0] = '0; + end + + addr_hit[7]: begin + reg_rdata_next[0] = '0; + reg_rdata_next[1] = '0; + reg_rdata_next[4:2] = fifo_ctrl_rxilvl_qs; + reg_rdata_next[6:5] = fifo_ctrl_txilvl_qs; + end + + addr_hit[8]: begin + reg_rdata_next[5:0] = fifo_status_txlvl_qs; + reg_rdata_next[21:16] = fifo_status_rxlvl_qs; + end + + addr_hit[9]: begin + reg_rdata_next[0] = ovrd_txen_qs; + reg_rdata_next[1] = ovrd_txval_qs; + end + + addr_hit[10]: begin + reg_rdata_next[15:0] = val_qs; + end + + addr_hit[11]: begin + reg_rdata_next[23:0] = timeout_ctrl_val_qs; + reg_rdata_next[31] = timeout_ctrl_en_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // // Assertions for Register Interface + // `ASSERT_PULSE(wePulse, reg_we) + // `ASSERT_PULSE(rePulse, reg_re) + + // `ASSERT(reAfterRv, $rose(reg_re || reg_we) |=> tl_o.d_valid) + + // `ASSERT(en2addrHit, (reg_we || reg_re) |-> $onehot0(addr_hit)) + + // // this is formulated as an assumption such that the FPV testbenches do disprove this + // // property by mistake + // `ASSUME(reqParity, tl_reg_h2d.a_valid |-> tl_reg_h2d.a_user.parity_en == 1'b0) + +endmodule
diff --git a/verilog/rtl/uart_rx.sv b/verilog/rtl/uart_rx.sv new file mode 100644 index 0000000..829895a --- /dev/null +++ b/verilog/rtl/uart_rx.sv
@@ -0,0 +1,105 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Description: UART Receive Module +// + +module uart_rx ( + input clk_i, + input rst_ni, + + input rx_enable, + input tick_baud_x16, + input parity_enable, + input parity_odd, + + output logic tick_baud, + output logic rx_valid, + output [7:0] rx_data, + output logic idle, + output frame_err, + output rx_parity_err, + + input rx +); + + logic rx_valid_q; + logic [10:0] sreg_q, sreg_d; + logic [3:0] bit_cnt_q, bit_cnt_d; + logic [3:0] baud_div_q, baud_div_d; + logic tick_baud_d, tick_baud_q; + logic idle_d, idle_q; + + assign tick_baud = tick_baud_q; + assign idle = idle_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + sreg_q <= 11'h0; + bit_cnt_q <= 4'h0; + baud_div_q <= 4'h0; + tick_baud_q <= 1'b0; + idle_q <= 1'b1; + end else begin + sreg_q <= sreg_d; + bit_cnt_q <= bit_cnt_d; + baud_div_q <= baud_div_d; + tick_baud_q <= tick_baud_d; + idle_q <= idle_d; + end + end + + always_comb begin + if (!rx_enable) begin + sreg_d = 11'h0; + bit_cnt_d = 4'h0; + baud_div_d = 4'h0; + tick_baud_d = 1'b0; + idle_d = 1'b1; + end else begin + tick_baud_d = 1'b0; + sreg_d = sreg_q; + bit_cnt_d = bit_cnt_q; + baud_div_d = baud_div_q; + idle_d = idle_q; + if (tick_baud_x16) begin + {tick_baud_d, baud_div_d} = {1'b0,baud_div_q} + 5'h1; + end + + if (idle_q && !rx) begin + // start of char, sample in the middle of the bit time + baud_div_d = 4'd8; + tick_baud_d = 1'b0; + bit_cnt_d = (parity_enable ? 4'd11 : 4'd10); + sreg_d = 11'h0; + idle_d = 1'b0; + end else if (!idle_q && tick_baud_q) begin + if ((bit_cnt_q == (parity_enable ? 4'd11 : 4'd10)) && rx) begin + // must have been a glitch on the input, start bit is not set + // in the middle of the bit time, abort + idle_d = 1'b1; + bit_cnt_d = 4'h0; + end else begin + sreg_d = {rx, sreg_q[10:1]}; + bit_cnt_d = bit_cnt_q - 4'h1; + idle_d = (bit_cnt_q == 4'h1); + end + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) rx_valid_q <= 1'b0; + else rx_valid_q <= tick_baud_q & (bit_cnt_q == 4'h1); + + end + + assign rx_valid = rx_valid_q; + assign rx_data = parity_enable ? sreg_q[8:1] : sreg_q[9:2]; + // (rx_parity = sreg_q[9]) + assign frame_err = rx_valid_q & ~sreg_q[10]; + assign rx_parity_err = parity_enable & rx_valid_q & + (^{sreg_q[9:1],parity_odd}); + +endmodule
diff --git a/verilog/rtl/uart_rx_prog.v b/verilog/rtl/uart_rx_prog.v new file mode 100644 index 0000000..fb43991 --- /dev/null +++ b/verilog/rtl/uart_rx_prog.v
@@ -0,0 +1,156 @@ + +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +// Set Parameter CLKS_PER_BIT as follows: +// CLKS_PER_BIT = (Frequency of i_Clock)/(Frequency of UART) +// Example: 10 MHz Clock, 115200 baud UART +// (10000000)/(115200) = 87 + +module uart_rx_prog ( + input clk_i, + input rst_ni, + input i_Rx_Serial, + input [15:0] CLKS_PER_BIT, + output o_Rx_DV, + output [7:0] o_Rx_Byte + ); + + parameter s_IDLE = 3'b000; + parameter s_RX_START_BIT = 3'b001; + parameter s_RX_DATA_BITS = 3'b010; + parameter s_RX_STOP_BIT = 3'b011; + parameter s_CLEANUP = 3'b100; + + reg r_Rx_Data_R ; + reg r_Rx_Data ; + + reg [15:0] r_Clock_Count ; + reg [2:0] r_Bit_Index ; //8 bits total + reg [7:0] r_Rx_Byte ; + reg r_Rx_DV ; + reg [2:0] r_SM_Main ; + + // Purpose: Double-register the incoming data. + // This allows it to be used in the UART RX Clock Domain. + // (It removes problems caused by metastability) + always @(posedge clk_i) + begin + if (~rst_ni) begin + r_Rx_Data_R <= 1'b1; + r_Rx_Data <= 1'b1; + end else begin + r_Rx_Data_R <= i_Rx_Serial; + r_Rx_Data <= r_Rx_Data_R; + end + end + + + // Purpose: Control RX state machine + always @(posedge clk_i or negedge rst_ni) + begin + if (~rst_ni) begin + r_SM_Main <= s_IDLE; + r_Rx_DV <= 1'b0; + r_Clock_Count <= 0; + r_Bit_Index <= 0; + end else begin + case (r_SM_Main) + s_IDLE : + begin + r_Rx_DV <= 1'b0; + r_Clock_Count <= 0; + r_Bit_Index <= 0; + + if (r_Rx_Data == 1'b0) // Start bit detected + r_SM_Main <= s_RX_START_BIT; + else + r_SM_Main <= s_IDLE; + end + + // Check middle of start bit to make sure it's still low + s_RX_START_BIT : + begin + if (r_Clock_Count == ((CLKS_PER_BIT-1)>>1)) + begin + if (r_Rx_Data == 1'b0) + begin + r_Clock_Count <= 0; // reset counter, found the middle + r_SM_Main <= s_RX_DATA_BITS; + end + else + r_SM_Main <= s_IDLE; + end + else + begin + r_Clock_Count <= r_Clock_Count + 1; + r_SM_Main <= s_RX_START_BIT; + end + end // case: s_RX_START_BIT + + + // Wait CLKS_PER_BIT-1 clock cycles to sample serial data + s_RX_DATA_BITS : + begin + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1; + r_SM_Main <= s_RX_DATA_BITS; + end + else + begin + r_Clock_Count <= 0; + r_Rx_Byte[r_Bit_Index] <= r_Rx_Data; + + // Check if we have received all bits + if (r_Bit_Index < 7) + begin + r_Bit_Index <= r_Bit_Index + 1; + r_SM_Main <= s_RX_DATA_BITS; + end + else + begin + r_Bit_Index <= 0; + r_SM_Main <= s_RX_STOP_BIT; + end + end + end // case: s_RX_DATA_BITS + + + // Receive Stop bit. Stop bit = 1 + s_RX_STOP_BIT : + begin + // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1; + r_SM_Main <= s_RX_STOP_BIT; + end + else + begin + r_Rx_DV <= 1'b1; + r_Clock_Count <= 0; + r_SM_Main <= s_CLEANUP; + end + end // case: s_RX_STOP_BIT + + + // Stay here 1 clock + s_CLEANUP : + begin + r_SM_Main <= s_IDLE; + r_Rx_DV <= 1'b0; + end + + + default : + r_SM_Main <= s_IDLE; + + endcase + end + end + + assign o_Rx_DV = r_Rx_DV; + assign o_Rx_Byte = r_Rx_Byte; + +endmodule // uart_rx
diff --git a/verilog/rtl/uart_tx.sv b/verilog/rtl/uart_tx.sv new file mode 100644 index 0000000..d10d16a --- /dev/null +++ b/verilog/rtl/uart_tx.sv
@@ -0,0 +1,79 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Description: UART Transmit Module +// + +module uart_tx ( + input clk_i, + input rst_ni, + + input tx_enable, + input tick_baud_x16, + input logic parity_enable, + + input wr, + input logic wr_parity, + input [7:0] wr_data, + output idle, + + output logic tx +); + + + logic [3:0] baud_div_q; + logic tick_baud_q; + + logic [3:0] bit_cnt_q, bit_cnt_d; + logic [10:0] sreg_q, sreg_d; + logic tx_q, tx_d; + + assign tx = tx_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + baud_div_q <= 4'h0; + tick_baud_q <= 1'b0; + end else if (tick_baud_x16) begin + {tick_baud_q, baud_div_q} <= {1'b0,baud_div_q} + 5'h1; + end else begin + tick_baud_q <= 1'b0; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + bit_cnt_q <= 4'h0; + sreg_q <= 11'h7ff; + tx_q <= 1'b1; + end else begin + bit_cnt_q <= bit_cnt_d; + sreg_q <= sreg_d; + tx_q <= tx_d; + end + end + + always_comb begin + if (!tx_enable) begin + bit_cnt_d = 4'h0; + sreg_d = 11'h7ff; + tx_d = 1'b1; + end else begin + bit_cnt_d = bit_cnt_q; + sreg_d = sreg_q; + tx_d = tx_q; + if (wr) begin + sreg_d = {1'b1, (parity_enable ? wr_parity : 1'b1), wr_data, 1'b0}; + bit_cnt_d = (parity_enable ? 4'd11 : 4'd10); + end else if (tick_baud_q && (bit_cnt_q != 4'h0)) begin + sreg_d = {1'b1, sreg_q[10:1]}; + tx_d = sreg_q[0]; + bit_cnt_d = bit_cnt_q - 4'h1; + end + end + end + + assign idle = (tx_enable) ? (bit_cnt_q == 4'h0) : 1'b1; + +endmodule