Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

hw: Add improved axi_to_reg adapter #62

Merged
merged 4 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export_include_dirs:
- hw/include

sources:
- hw/future/axi_to_reg_v2.sv
- hw/bootrom/cheshire_bootrom.sv
- hw/regs/cheshire_reg_pkg.sv
- hw/regs/cheshire_reg_top.sv
Expand Down
65 changes: 19 additions & 46 deletions hw/cheshire_soc.sv
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,12 @@ module cheshire_soc import cheshire_pkg::*; #(
axi_slv_req_t axi_reg_amo_req, axi_reg_cut_req;
axi_slv_rsp_t axi_reg_amo_rsp, axi_reg_cut_rsp;

axi_d32_req_t axi_reg_d32_req;
axi_d32_rsp_t axi_reg_d32_rsp;

reg_req_t reg_in_req;
reg_rsp_t reg_in_rsp;

logic[AxiSlvIdWidth-1:0] reg_id;

reg_req_t [RegOut.num_out-1:0] reg_out_req;
reg_rsp_t [RegOut.num_out-1:0] reg_out_rsp;

Expand Down Expand Up @@ -359,53 +359,26 @@ module cheshire_soc import cheshire_pkg::*; #(
.mst_resp_i ( axi_reg_cut_rsp )
);

// Convert to 32-bit reg datawidth
axi_dw_converter #(
.AxiSlvPortDataWidth ( Cfg.AxiDataWidth ),
.AxiMstPortDataWidth ( 32 ),
.AxiAddrWidth ( Cfg.AddrWidth ),
.AxiIdWidth ( AxiSlvIdWidth ),
.aw_chan_t ( axi_slv_aw_chan_t ),
.mst_w_chan_t ( axi_d32_w_chan_t ),
.slv_w_chan_t ( axi_slv_w_chan_t ),
.b_chan_t ( axi_slv_b_chan_t ),
.ar_chan_t ( axi_slv_ar_chan_t ),
.mst_r_chan_t ( axi_d32_r_chan_t ),
.slv_r_chan_t ( axi_slv_r_chan_t ),
.axi_mst_req_t ( axi_d32_req_t ),
.axi_mst_resp_t ( axi_d32_rsp_t ),
.axi_slv_req_t ( axi_slv_req_t ),
.axi_slv_resp_t ( axi_slv_rsp_t )
) i_reg_axi_dw_converter (
.clk_i,
.rst_ni,
.slv_req_i ( axi_reg_cut_req ),
.slv_resp_o ( axi_reg_cut_rsp ),
.mst_req_o ( axi_reg_d32_req ),
.mst_resp_i ( axi_reg_d32_rsp )
);

// Convert from AXI to reg protocol
axi_to_reg #(
.ADDR_WIDTH ( Cfg.AddrWidth ),
.DATA_WIDTH ( 32 ),
.ID_WIDTH ( AxiSlvIdWidth ),
.USER_WIDTH ( Cfg.AxiUserWidth ),
.AXI_MAX_WRITE_TXNS ( Cfg.RegMaxReadTxns ),
.AXI_MAX_READ_TXNS ( Cfg.RegMaxWriteTxns ),
.DECOUPLE_W ( 1 ),
.axi_req_t ( axi_d32_req_t ),
.axi_rsp_t ( axi_d32_rsp_t ),
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t )
) i_reg_axi_to_reg (
axi_to_reg_v2 #(
.AxiAddrWidth ( Cfg.AddrWidth ),
.AxiDataWidth ( Cfg.AxiDataWidth ),
.AxiIdWidth ( AxiSlvIdWidth ),
.AxiUserWidth ( Cfg.AxiUserWidth ),
.RegDataWidth ( 32'd32 ),
.axi_req_t ( axi_slv_req_t ),
.axi_rsp_t ( axi_slv_rsp_t ),
.reg_req_t ( reg_req_t ),
.reg_rsp_t ( reg_rsp_t )
) i_axi_to_reg_v2 (
.clk_i,
.rst_ni,
.testmode_i ( test_mode_i ),
.axi_req_i ( axi_reg_d32_req ),
.axi_rsp_o ( axi_reg_d32_rsp ),
.reg_req_o ( reg_in_req ),
.reg_rsp_i ( reg_in_rsp )
.axi_req_i ( axi_reg_cut_req ),
.axi_rsp_o ( axi_reg_cut_rsp ),
.reg_req_o ( reg_in_req ),
.reg_rsp_i ( reg_in_rsp ),
.reg_id_o ( reg_id ),
.busy_o ( )
);

// Non-matching addresses are directed to an error slave
Expand Down
170 changes: 170 additions & 0 deletions hw/future/axi_to_reg_v2.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright 2023 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51
//
// Thomas Benz <[email protected]>

`include "axi/typedef.svh"

/// Version 2 of a protocol converter from AXI4 to the register interface.
/// AXI Data Width >= Reg Data Width
module axi_to_reg_v2 #(
/// The width of the address.
parameter int unsigned AxiAddrWidth = 32'd0,
/// The width of the data.
parameter int unsigned AxiDataWidth = 32'd0,
/// The width of the id.
parameter int unsigned AxiIdWidth = 32'd0,
/// The width of the user signal.
parameter int unsigned AxiUserWidth = 32'd0,
/// The data width of the Reg bus
parameter int unsigned RegDataWidth = 32'd0,
/// AXI request struct type.
parameter type axi_req_t = logic,
/// AXI response struct type.
parameter type axi_rsp_t = logic,
/// Regbus request struct type.
parameter type reg_req_t = logic,
/// Regbus response struct type.
parameter type reg_rsp_t = logic,
/// Dependent parameter: ID Width
parameter type id_t = logic[AxiIdWidth-1:0]
)(
input logic clk_i,
input logic rst_ni,
input axi_req_t axi_req_i,
output axi_rsp_t axi_rsp_o,
output reg_req_t reg_req_o,
input reg_rsp_t reg_rsp_i,
output id_t reg_id_o,
output logic busy_o
);

// how many times is the AXI bus wider than the regbus?
localparam int unsigned NumBanks = AxiDataWidth / RegDataWidth;

localparam type addr_t = logic [AxiAddrWidth-1 :0];
localparam type reg_data_t = logic [RegDataWidth-1 :0];
localparam type reg_strb_t = logic [RegDataWidth/8-1:0];

// TCDM BUS
logic [NumBanks-1:0] mem_req;
logic [NumBanks-1:0] mem_gnt;
addr_t [NumBanks-1:0] mem_addr;
reg_data_t [NumBanks-1:0] mem_wdata;
reg_strb_t [NumBanks-1:0] mem_strb;
logic [NumBanks-1:0] mem_we;
id_t [NumBanks-1:0] mem_id;
logic [NumBanks-1:0] mem_rvalid;
reg_data_t [NumBanks-1:0] mem_rdata;
logic [NumBanks-1:0] mem_err;

// sub reg buses
reg_req_t [NumBanks-1:0] reg_req, valid_req, zero_w_req;
reg_rsp_t [NumBanks-1:0] reg_rsp, valid_rsp, zero_w_rsp;

// convert to TCDM first
axi_to_detailed_mem #(
.axi_req_t ( axi_req_t ),
.axi_resp_t ( axi_rsp_t ),
.AddrWidth ( AxiAddrWidth ),
.DataWidth ( AxiDataWidth ),
.IdWidth ( AxiIdWidth ),
.UserWidth ( AxiUserWidth ),
.NumBanks ( NumBanks ),
.BufDepth ( 32'd1 ),
.HideStrb ( 1'b0 ),
.OutFifoDepth ( 32'd1 )
) i_axi_to_detailed_mem (
.clk_i,
.rst_ni,
.busy_o,
.axi_req_i,
.axi_resp_o ( axi_rsp_o ),
.mem_req_o ( mem_req ),
.mem_gnt_i ( mem_gnt ),
.mem_addr_o ( mem_addr ),
.mem_wdata_o ( mem_wdata ),
.mem_strb_o ( mem_strb ),
.mem_atop_o ( /* NOT CONNECTED */ ),
.mem_lock_o ( /* NOT CONNECTED */ ),
.mem_we_o ( mem_we ),
.mem_id_o ( mem_id ),
.mem_user_o ( /* NOT CONNECTED */ ),
.mem_cache_o ( /* NOT CONNECTED */ ),
.mem_prot_o ( /* NOT CONNECTED */ ),
.mem_qos_o ( /* NOT CONNECTED */ ),
.mem_region_o ( /* NOT CONNECTED */ ),
.mem_rvalid_i ( mem_rvalid ),
.mem_rdata_i ( mem_rdata ),
.mem_err_i ( mem_err ),
.mem_exokay_i ( '0 )
);

// every subbus is converted independently
for (genvar i = 0; i < NumBanks; i++) begin : gen_tcdm_to_reg
periph_to_reg #(
.AW ( AxiAddrWidth ),
.DW ( RegDataWidth ),
.IW ( AxiIdWidth ),
.req_t ( reg_req_t ),
.rsp_t ( reg_rsp_t )
) i_periph_to_reg (
.clk_i,
.rst_ni,
.req_i ( mem_req [i] ),
.add_i ( mem_addr [i] ),
.wen_i ( !mem_we [i] ),
.wdata_i ( mem_wdata [i] ),
.be_i ( mem_strb [i] ),
.id_i ( '0 ),
.gnt_o ( mem_gnt [i] ),
.r_rdata_o ( mem_rdata [i] ),
.r_opc_o ( mem_err [i] ),
.r_id_o ( /* NOT CONNECTED */ ),
.r_valid_o ( mem_rvalid [i] ),
.reg_req_o ( reg_req [i] ),
.reg_rsp_i ( reg_rsp [i] )
);

// filter zero strobe writes early, directly ack them
reg_demux #(
.NoPorts ( 32'd2 ),
.req_t ( reg_req_t ),
.rsp_t ( reg_rsp_t )
) i_reg_demux (
.clk_i,
.rst_ni,
.in_select_i ( reg_req[i].write & (reg_req[i].wstrb == '0) ),
.in_req_i ( reg_req[i] ),
.in_rsp_o ( reg_rsp[i] ),
.out_req_o ( {zero_w_req[i], valid_req[i]} ),
.out_rsp_i ( {zero_w_rsp[i], valid_rsp[i]} )
);

// ack zero strobe writes here
assign zero_w_rsp[i].ready = 1'b1;
assign zero_w_rsp[i].error = 1'b0;
assign zero_w_rsp[i].rdata = '0;
end

// arbitrate over valid accesses in sub buses
reg_mux #(
.NoPorts( NumBanks ),
.AW ( AxiAddrWidth ),
.DW ( AxiDataWidth ),
.req_t ( reg_req_t ),
.rsp_t ( reg_rsp_t )
) i_reg_mux (
.clk_i,
.rst_ni,
.in_req_i ( valid_req ),
.in_rsp_o ( valid_rsp ),
.out_req_o ( reg_req_o ),
.out_rsp_i ( reg_rsp_i )
);

// forward the id, all banks carry the same ID here
assign reg_id_o = mem_id[0];

endmodule
Loading