// ============================================================================ // cv_x1_pro_axi -- AXI4-Lite / AXI4-Stream integration wrapper for the // CV-X1 Pro L8 subsystem (module cv_x1_pro_subsystem, public/forge/source/dut.v). // Corvel / RISCON Labs. Verilog-2005. Single clock (aclk), active-low // asynchronous reset (aresetn); the integrator must synchronize the release // (deassertion) of aresetn to aclk. // // Interfaces // s_axil_* AXI4-Lite slave, 32-bit data, 12-bit byte address (4 KB window) // s_axis_* AXI4-Stream slave : query (TDATA 64) + tile descriptor (TUSER 20) // m_axis_* AXI4-Stream master: accumulated result (TDATA 32), TUSER = bank // // The core is instantiated unmodified. The wrapper adds the register file, // the weight-memory write path, a stream enable and an in-flight counter. // Register map: integration/regmap/cv_x1_pro_axi.regmap.md // ============================================================================ `timescale 1ns/1ps module cv_x1_pro_axi ( input wire aclk, input wire aresetn, // ---------------- AXI4-Lite slave ---------------- input wire [11:0] s_axil_awaddr, input wire [2:0] s_axil_awprot, input wire s_axil_awvalid, output wire s_axil_awready, input wire [31:0] s_axil_wdata, input wire [3:0] s_axil_wstrb, input wire s_axil_wvalid, output wire s_axil_wready, output reg [1:0] s_axil_bresp, output reg s_axil_bvalid, input wire s_axil_bready, input wire [11:0] s_axil_araddr, input wire [2:0] s_axil_arprot, input wire s_axil_arvalid, output wire s_axil_arready, output reg [31:0] s_axil_rdata, output reg [1:0] s_axil_rresp, output reg s_axil_rvalid, input wire s_axil_rready, // ---------------- AXI4-Stream slave (query + descriptor) ---------------- input wire s_axis_tvalid, output wire s_axis_tready, input wire [63:0] s_axis_tdata, input wire [19:0] s_axis_tuser, input wire s_axis_tlast, // ---------------- AXI4-Stream master (result) ---------------- output wire m_axis_tvalid, input wire m_axis_tready, output wire [31:0] m_axis_tdata, output wire [1:0] m_axis_tuser, output wire m_axis_tlast ); // ------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------ localparam [1:0] RESP_OKAY = 2'b00; localparam [1:0] RESP_SLVERR = 2'b10; localparam [31:0] ID_VALUE = 32'h4356_5831; // "CVX1" localparam [31:0] VERSION_VALUE = 32'h0001_0000; // wrapper 1.0.0 // CAPS: LANES=8 [7:0], BANKS=4 [11:8], ROWS_PER_BANK=16 [19:12], // FIFO_DEPTH=4 [23:20], PIPE_STAGES=3 [27:24], SAT_MODE=1 [28], // WEIGHT_READBACK=0 [29] localparam [31:0] CAPS_VALUE = 32'h1341_0408; // Word addresses (byte address [11:2]) localparam [9:0] A_ID = 10'h000; // 0x000 localparam [9:0] A_VERSION = 10'h001; // 0x004 localparam [9:0] A_CAPS = 10'h002; // 0x008 localparam [9:0] A_CTRL = 10'h003; // 0x00C localparam [9:0] A_STATUS = 10'h004; // 0x010 localparam [9:0] A_ERR = 10'h005; // 0x014 localparam [9:0] A_PERF_MAC = 10'h006; // 0x018 localparam [9:0] A_PERF_STL = 10'h007; // 0x01C localparam [9:0] A_PERF_BNK = 10'h008; // 0x020 // Weight window 0x100..0x1FC : word addresses 0x040..0x07F // ------------------------------------------------------------------------ // Core interface // ------------------------------------------------------------------------ wire core_s_tready; wire core_m_tvalid; wire [31:0] core_m_tdata; wire core_m_tlast; wire [1:0] core_m_tbank; wire [2:0] core_fifo_count; wire core_sat_sticky; wire [15:0] core_perf_mac; wire [15:0] core_perf_stall; wire [15:0] core_perf_bank; reg [1:0] ctrl_q; // [0] STREAM_EN, [1] WGUARD reg [2:0] err_q; // [0] WGUARD_REJECT, [1] WSTRB_REJECT, [2] ADDR_ERR reg [3:0] inflight_q; // tiles accepted on s_axis and not yet delivered on m_axis wire stream_en = ctrl_q[0]; wire wguard = ctrl_q[1]; wire busy = (inflight_q != 4'd0); // Ingress gating by STREAM_EN (TREADY/TVALID are both gated so that no // transfer is accepted while the stream is disabled). wire core_s_tvalid = s_axis_tvalid & stream_en; assign s_axis_tready = core_s_tready & stream_en; assign m_axis_tvalid = core_m_tvalid; assign m_axis_tdata = core_m_tdata; assign m_axis_tlast = core_m_tlast; assign m_axis_tuser = core_m_tbank; wire s_hs = s_axis_tvalid & s_axis_tready; wire m_hs = core_m_tvalid & m_axis_tready; // ------------------------------------------------------------------------ // AXI4-Lite write channel: independent AW / W holding registers // ------------------------------------------------------------------------ reg aw_full_q; reg [9:0] aw_word_q; reg w_full_q; reg [31:0] w_data_q; reg [3:0] w_strb_q; assign s_axil_awready = ~aw_full_q; assign s_axil_wready = ~w_full_q; // A write executes once both address and data are held and the previous // response has been accepted. wire wr_fire = aw_full_q & w_full_q & ~s_axil_bvalid; wire wr_is_weight = (aw_word_q[9:6] == 4'h1); // 0x100..0x1FC wire wr_is_ctrl = (aw_word_q == A_CTRL); wire wr_is_err = (aw_word_q == A_ERR); wire wr_strb_full = (w_strb_q == 4'hF); wire wr_guarded = wguard & busy; wire wr_weight_ok = wr_is_weight & wr_strb_full & ~wr_guarded; wire wr_ok = wr_weight_ok | wr_is_ctrl | wr_is_err; wire core_sram_we = wr_fire & wr_weight_ok; wire [5:0] core_sram_waddr = aw_word_q[5:0]; wire [31:0] core_sram_wdata = w_data_q; always @(posedge aclk or negedge aresetn) begin if (!aresetn) begin aw_full_q <= 1'b0; aw_word_q <= 10'd0; w_full_q <= 1'b0; w_data_q <= 32'd0; w_strb_q <= 4'd0; s_axil_bvalid <= 1'b0; s_axil_bresp <= RESP_OKAY; ctrl_q <= 2'b10; // STREAM_EN=0, WGUARD=1 err_q <= 3'b000; end else begin if (s_axil_awvalid && !aw_full_q) begin aw_full_q <= 1'b1; aw_word_q <= s_axil_awaddr[11:2]; end if (s_axil_wvalid && !w_full_q) begin w_full_q <= 1'b1; w_data_q <= s_axil_wdata; w_strb_q <= s_axil_wstrb; end if (s_axil_bvalid && s_axil_bready) begin s_axil_bvalid <= 1'b0; end if (wr_fire) begin aw_full_q <= 1'b0; w_full_q <= 1'b0; s_axil_bvalid <= 1'b1; s_axil_bresp <= wr_ok ? RESP_OKAY : RESP_SLVERR; if (wr_is_ctrl && w_strb_q[0]) begin ctrl_q <= w_data_q[1:0]; end if (wr_is_err && w_strb_q[0]) begin err_q <= err_q & ~w_data_q[2:0]; end if (wr_is_weight && !wr_strb_full) begin err_q[1] <= 1'b1; end if (wr_is_weight && wr_strb_full && wr_guarded) begin err_q[0] <= 1'b1; end if (!wr_is_weight && !wr_is_ctrl && !wr_is_err) begin err_q[2] <= 1'b1; end end end end // ------------------------------------------------------------------------ // AXI4-Lite read channel // ------------------------------------------------------------------------ assign s_axil_arready = ~s_axil_rvalid; wire [9:0] ar_word = s_axil_araddr[11:2]; wire [31:0] status_value = {20'd0, core_m_tvalid, // [11] M_TVALID core_s_tready, // [10] CORE_S_TREADY core_sat_sticky, // [9] SAT_OVF busy, // [8] BUSY 1'b0, // [7] reserved core_fifo_count, // [6:4] FIFO_COUNT inflight_q}; // [3:0] INFLIGHT reg [31:0] rd_value; reg rd_ok; always @(*) begin rd_value = 32'd0; rd_ok = 1'b1; if (ar_word[9:6] == 4'h1) begin rd_value = 32'd0; // weight window: write-only end else begin case (ar_word) A_ID: rd_value = ID_VALUE; A_VERSION: rd_value = VERSION_VALUE; A_CAPS: rd_value = CAPS_VALUE; A_CTRL: rd_value = {30'd0, ctrl_q}; A_STATUS: rd_value = status_value; A_ERR: rd_value = {29'd0, err_q}; A_PERF_MAC: rd_value = {16'd0, core_perf_mac}; A_PERF_STL: rd_value = {16'd0, core_perf_stall}; A_PERF_BNK: rd_value = {16'd0, core_perf_bank}; default: begin rd_value = 32'd0; rd_ok = 1'b0; end endcase end end always @(posedge aclk or negedge aresetn) begin if (!aresetn) begin s_axil_rvalid <= 1'b0; s_axil_rdata <= 32'd0; s_axil_rresp <= RESP_OKAY; end else begin if (s_axil_arvalid && !s_axil_rvalid) begin s_axil_rvalid <= 1'b1; s_axil_rdata <= rd_value; s_axil_rresp <= rd_ok ? RESP_OKAY : RESP_SLVERR; end else if (s_axil_rvalid && s_axil_rready) begin s_axil_rvalid <= 1'b0; end end end // ------------------------------------------------------------------------ // In-flight tile counter (each accepted tile yields exactly one result) // ------------------------------------------------------------------------ always @(posedge aclk or negedge aresetn) begin if (!aresetn) begin inflight_q <= 4'd0; end else begin case ({s_hs, m_hs}) 2'b10: inflight_q <= inflight_q + 4'd1; 2'b01: inflight_q <= inflight_q - 4'd1; default: inflight_q <= inflight_q; endcase end end // ------------------------------------------------------------------------ // Core (unmodified) // ------------------------------------------------------------------------ cv_x1_pro_subsystem u_core ( .clk (aclk), .rst_n (aresetn), .sram_we (core_sram_we), .sram_waddr (core_sram_waddr), .sram_wdata (core_sram_wdata), .s_axis_tvalid (core_s_tvalid), .s_axis_tready (core_s_tready), .s_axis_tdata (s_axis_tdata), .s_axis_tuser (s_axis_tuser), .s_axis_tlast (s_axis_tlast), .m_axis_tvalid (core_m_tvalid), .m_axis_tready (m_axis_tready), .m_axis_tdata (core_m_tdata), .m_axis_tlast (core_m_tlast), .m_axis_tbank (core_m_tbank), .fifo_count (core_fifo_count), .sat_overflow_sticky (core_sat_sticky), .perf_mac_tiles (core_perf_mac), .perf_stall_cycles (core_perf_stall), .perf_bank_conflicts (core_perf_bank) ); endmodule