1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-17 20:02:53 +08:00
oh/elink/hdl/esaxi.v
Andreas Olofsson 93330039e0 AXI bug fixes
-First bug was a typo. Cursing AXI for making every signal look exactly the same at first glance.  Not good use practice
-Second bug was sloppy. (removed pipeline stage on write data by mistake)
2015-04-14 20:26:58 -04:00

574 lines
19 KiB
Verilog

module esaxi (/*autoarg*/
// Outputs
emwr_access, emwr_write, emwr_datamode, emwr_ctrlmode,
emwr_dstaddr, emwr_data, emwr_srcaddr, emrq_access, emrq_write,
emrq_datamode, emrq_ctrlmode, emrq_dstaddr, emrq_data,
emrq_srcaddr, emrr_rd_en, mi_clk, mi_rx_emmu_sel, mi_tx_emmu_sel,
mi_ecfg_sel, mi_embox_sel, mi_we, mi_addr, mi_din, s_axi_arready,
s_axi_awready, s_axi_bresp, s_axi_bvalid, s_axi_rdata, s_axi_rlast,
s_axi_rresp, s_axi_rvalid, s_axi_wready,
// Inputs
emwr_progfull, emrq_progfull, emrr_data, emrr_access, mi_ecfg_dout,
mi_tx_emmu_dout, mi_rx_emmu_dout, mi_embox_dout, ecfg_tx_ctrlmode,
ecfg_coreid, ecfg_timeout_enable, s_axi_aclk, s_axi_aresetn,
s_axi_araddr, s_axi_arburst, s_axi_arcache, s_axi_arlen,
s_axi_arprot, s_axi_arqos, s_axi_arsize, s_axi_arvalid,
s_axi_awaddr, s_axi_awburst, s_axi_awcache, s_axi_awlen,
s_axi_awprot, s_axi_awqos, s_axi_awsize, s_axi_awvalid,
s_axi_bready, s_axi_rready, s_axi_wdata, s_axi_wlast, s_axi_wstrb,
s_axi_wvalid
);
parameter [11:0] c_read_tag_addr = 12'h810;//emesh srcaddr tag
parameter integer c_s_axi_addr_width = 30; //address width
parameter [11:0] ELINKID = 12'h810;
/*****************************/
/*Write request for TX fifo */
/*****************************/
output emwr_access;
output emwr_write;
output [1:0] emwr_datamode;
output [3:0] emwr_ctrlmode;
output [31:0] emwr_dstaddr;
output [31:0] emwr_data;
output [31:0] emwr_srcaddr;
input emwr_progfull;
/*****************************/
/*Read request for TX fifo */
/*****************************/
output emrq_access;
output emrq_write;
output [1:0] emrq_datamode;
output [3:0] emrq_ctrlmode;
output [31:0] emrq_dstaddr;
output [31:0] emrq_data;
output [31:0] emrq_srcaddr;
input emrq_progfull; //TODO? used for?
/*****************************/
/*Read response from RX fifo */
/*****************************/
//Only data needed
input [31:0] emrr_data;
input emrr_access;
output emrr_rd_en; //update read fifo
/*****************************/
/*Register RD/WR Interface */
/*****************************/
output mi_clk;
output mi_rx_emmu_sel;
output mi_tx_emmu_sel;
output mi_ecfg_sel;
output mi_embox_sel;
output mi_we;
output [19:0] mi_addr;
output [31:0] mi_din;
input [31:0] mi_ecfg_dout;
input [31:0] mi_tx_emmu_dout;
input [31:0] mi_rx_emmu_dout;
input [31:0] mi_embox_dout;
/*****************************/
/*Config Settings */
/*****************************/
input [3:0] ecfg_tx_ctrlmode;
input [11:0] ecfg_coreid;
input ecfg_timeout_enable;
/*****************************/
/*AXI slave interface */
/*****************************/
//Clock and reset
input s_axi_aclk;
input s_axi_aresetn;
//Read address channel
input [29:0] s_axi_araddr;
input [1:0] s_axi_arburst;
input [3:0] s_axi_arcache;
input [7:0] s_axi_arlen;
input [2:0] s_axi_arprot;
input [3:0] s_axi_arqos;
output s_axi_arready;
input [2:0] s_axi_arsize;
input s_axi_arvalid;
//Write address channel
input [29:0] s_axi_awaddr;
input [1:0] s_axi_awburst;
input [3:0] s_axi_awcache;
input [7:0] s_axi_awlen;
input [2:0] s_axi_awprot;
input [3:0] s_axi_awqos;
output s_axi_awready;
input [2:0] s_axi_awsize;
input s_axi_awvalid;
//Buffered write response channel
input s_axi_bready;
output [1:0] s_axi_bresp;
output s_axi_bvalid;
//Read channel
output [31:0] s_axi_rdata;
output s_axi_rlast;
input s_axi_rready;
output [1:0] s_axi_rresp;
output s_axi_rvalid;
//Write channel
input [31:0] s_axi_wdata;
input s_axi_wlast;
output s_axi_wready;
input [3:0] s_axi_wstrb;
input s_axi_wvalid;
// axi4full signals
reg s_axi_awready;
reg s_axi_wready;
reg s_axi_bvalid;
reg [1:0] s_axi_bresp;
reg s_axi_arready;
reg [31:0] emwr_data_reg;
reg [31:0] emwr_dstaddr_reg;
reg [3:0] emwr_ctrlmode_reg;
reg [1:0] emwr_datamode_reg;
reg [31:0] axi_awaddr; // 32b for epiphany addr
reg [1:0] axi_awburst;
reg [2:0] axi_awsize;
reg [31:0] axi_araddr;
reg [7:0] axi_arlen;
reg [1:0] axi_arburst;
reg [2:0] axi_arsize;
reg [31:0] s_axi_rdata;
reg [1:0] s_axi_rresp;
reg s_axi_rlast;
reg s_axi_rvalid;
reg read_active;
reg [31:0] read_addr;
reg write_active;
reg b_wait; // waiting to issue write response (unlikely?)
reg emwr_access;
reg [3:0] emwr_ctrlmode;
reg [1:0] emwr_datamode;
reg [31:0] emwr_dstaddr;
reg [31:0] emwr_data;
reg [31:0] emwr_srcaddr; //upper 32 bits in case 64 bit writes are supported
reg emrq_access;
reg [3:0] emrq_ctrlmode;
reg [1:0] emrq_datamode;
reg [31:0] emrq_dstaddr;
reg [31:0] emrq_srcaddr; //upper 32 bits in case 64 bit writes are supported
reg pre_wr_en; // delay for data alignment
reg ractive_reg; // need leading edge of active for 1st req
reg rnext;
reg mi_rx_emmu_reg;
reg mi_tx_emmu_reg;
reg mi_ecfg_reg;
reg mi_embox_reg;
reg mi_rd_reg;
wire last_wr_beat;
wire last_rd_beat;
wire mi_wr;
wire mi_rd;
wire mi_we;
wire mi_en;
wire [31:0] mi_dout;
wire mi_sel;
wire [31:0] emrr_mux_data;
//local parameter for addressing 32 bit / 64 bit c_s_axi_data_width
//addr_lsb is used for addressing 32/64 bit registers/memories
//addr_lsb = 2 for 32 bits (n downto 2)
//addr_lsb = 3 for 64 bits (n downto 3)
//TODO? Do we really need this?
localparam integer addr_lsb = 2;
//###################################################
//#WRITE ADDRESS CHANNEL
//###################################################
assign last_wr_beat = s_axi_wready & s_axi_wvalid & s_axi_wlast;
// axi_awready is asserted when there is no write transfer in progress
always @(posedge s_axi_aclk )
begin
if(~s_axi_aresetn)
begin
s_axi_awready <= 1'b0;
write_active <= 1'b0;
end
else
begin
// we're always ready for an address cycle if we're not doing something else
// note: might make this faster by going ready on last beat instead of after,
// but if we want the very best each channel should be fifo'd.
if( ~s_axi_awready & ~write_active & ~b_wait )
s_axi_awready <= 1'b1;
else if( s_axi_awvalid )
s_axi_awready <= 1'b0;
// the write cycle is "active" as soon as we capture an address, it
// ends on the last beat.
if( s_axi_awready & s_axi_awvalid )
write_active <= 1'b1;
else if( last_wr_beat )
write_active <= 1'b0;
end // else: !if(~s_axi_aresetn)
end // always @ (posedge s_axi_aclk )
// capture address & other aw info, update address during cycle
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
//s_axi_bid <= 'd0; // capture for write response
axi_awaddr[31:0] <= 32'd0;
axi_awsize[2:0] <= 3'd0;
axi_awburst[1:0] <= 2'd0;
end
else
begin
if( s_axi_awready & s_axi_awvalid )
begin
//TODO: If we support only one host read, why the need?
//s_axi_bid <= s_axi_awid;
//TODO: something is wrong here!!!
axi_awaddr[31:0] <= { ecfg_coreid[11:c_s_axi_addr_width-20],
s_axi_awaddr[29:0] };
axi_awsize <= s_axi_awsize; // 0=byte, 1=16b, 2=32b
axi_awburst <= s_axi_awburst; // type, 0=fixed, 1=incr, 2=wrap
end
else if( s_axi_wvalid & s_axi_wready )
if( axi_awburst == 2'b01 )
begin //incremental burst
// the write address for all the beats in the transaction are increments by the data width.
// note: this should be based on awsize instead to support narrow bursts, i think.
//TODO: BUG!!
axi_awaddr[31:addr_lsb] <= axi_awaddr[31:addr_lsb] + 30'd1;
//awaddr alignedto data width
axi_awaddr[addr_lsb-1:0] <= {addr_lsb{1'b0}};
end // both fixed & wrapping types are treated as fixed, no update.
end // else: !if(~s_axi_aresetn)
//###################################################
//#WRITE CHANNEL
//###################################################
always @ (posedge s_axi_aclk)
if(~s_axi_aresetn)
s_axi_wready <= 1'b0;
else
begin
if( last_wr_beat )
s_axi_wready <= 1'b0;
else if( write_active )
s_axi_wready <= ~emwr_progfull;
end
// implement write response logic generation
// the write response and response valid signals are asserted by the slave
// at the end of each transaction, burst or single.
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
s_axi_bvalid <= 1'b0;
s_axi_bresp[1:0] <= 2'b0;
b_wait <= 1'b0;
end
else
begin
if( last_wr_beat )
begin
s_axi_bvalid <= 1'b1;
s_axi_bresp[1:0] <= 2'b0; // 'okay' response
b_wait <= ~s_axi_bready; // note: assumes bready will not drop without valid?
end
else if (s_axi_bready & s_axi_bvalid)
begin
s_axi_bvalid <= 1'b0;
b_wait <= 1'b0;
end
end // else: !if( s_axi_aresetn == 1'b0 )
//###################################################
//#READ REQUEST CHANNEL
//###################################################
assign last_rd_beat = s_axi_rvalid & s_axi_rlast & s_axi_rready;
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
s_axi_arready <= 1'b0;
read_active <= 1'b0;
end
else
begin
//arready
if( ~s_axi_arready & ~read_active )
s_axi_arready <= 1'b1;
else if( s_axi_arvalid )
s_axi_arready <= 1'b0;
//read_active
if( s_axi_arready & s_axi_arvalid )
read_active <= 1'b1;
else if( last_rd_beat )
read_active <= 1'b0;
end // else: !if( s_axi_aresetn == 1'b0 )
//Read address channel state machine
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
axi_araddr[31:0] <= 0;
axi_arlen <= 8'd0;
axi_arburst <= 2'd0;
axi_arsize[2:0] <= 3'b0;
s_axi_rlast <= 1'b0;
//s_axi_rid <= 'd0;
end
else
begin
if( s_axi_arready & s_axi_arvalid )
begin
//TODO: something is wrong..
axi_araddr[31:0] <= { ecfg_coreid[11:c_s_axi_addr_width-20],
s_axi_araddr[29:0] }; // start address of transfer
axi_arlen <= s_axi_arlen;
axi_arburst <= s_axi_arburst;
axi_arsize <= s_axi_arsize;
s_axi_rlast <= ~(|s_axi_arlen);
//s_axi_rid <= s_axi_arid;
end
else if( s_axi_rvalid & s_axi_rready)
begin
axi_arlen <= axi_arlen - 1;
if(axi_arlen == 8'd1)
s_axi_rlast <= 1'b1;
if( s_axi_arburst == 2'b01)
begin //incremental burst
// the read address for all the beats in the transaction are increments by awsize
// note: this should be based on awsize instead to support narrow bursts, i think?
axi_araddr[c_s_axi_addr_width - 1:addr_lsb] <= axi_araddr[c_s_axi_addr_width - 1:addr_lsb] + 1;
//araddr aligned to 4 byte boundary
axi_araddr[addr_lsb-1:0] <= {addr_lsb{1'b0}};
//for awsize = 4 bytes (010)
end
end // if ( s_axi_rvalid & s_axi_rready)
end // else: !if( s_axi_aresetn == 1'b0 )
//###################################################
//#WRITE DATA
//###################################################
assign emwr_write = 1'b1;
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
emwr_data_reg[31:0] <= 32'd0;
emwr_dstaddr_reg[31:0] <= 32'd0;
emwr_ctrlmode_reg[3:0] <= 4'd0;
emwr_datamode_reg[1:0] <= 2'd0;
emwr_access <= 1'b0;
pre_wr_en <= 1'b0;
end
else
begin
pre_wr_en <= s_axi_wready & s_axi_wvalid;
emwr_access <= pre_wr_en;
emwr_ctrlmode_reg[3:0] <= ecfg_tx_ctrlmode[3:0];//static
emwr_datamode_reg[1:0] <= axi_awsize[1:0];
emwr_dstaddr_reg[31:2] <= axi_awaddr[31:2]; //set lsbs of address based on write strobes
casez(s_axi_wstrb[3:0])
4'b???1://aligned
begin
emwr_data_reg[31:0] <= s_axi_wdata[31:0];
emwr_dstaddr_reg[1:0] <= 2'd0;
end
4'b??10 : //shift by byte
begin
emwr_data_reg[31:0] <= {8'd0, s_axi_wdata[31:8]};
emwr_dstaddr_reg[1:0] <= 2'd1;
end
4'b?100 : //shift by two bytes
begin
emwr_data_reg[31:0] <= {16'd0, s_axi_wdata[31:16]};
emwr_dstaddr_reg[1:0] <= 2'd2;
end
default: //shift by three bytes
begin
emwr_data_reg[31:0] <= {24'd0, s_axi_wdata[31:24]};
emwr_dstaddr_reg[1:0] <= 2'd3;
end
endcase // casez (s_axi_wstrb[3:0])
end // else: !if(~s_axi_aresetn)
//Pipeline stage
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
emwr_srcaddr[31:0] <= 32'd0;
emwr_data[31:0] <= 32'd0;
emwr_dstaddr[31:0] <= 32'd0;
emwr_ctrlmode[3:0] <= 4'd0;
emwr_datamode[1:0] <= 2'd0;
emwr_access <= 1'b0;
end
else
begin
emwr_srcaddr[31:0] <= 32'b0;
emwr_data[31:0] <= emwr_data_reg[31:0];
emwr_dstaddr[31:0] <= emwr_dstaddr_reg[31:0];
emwr_ctrlmode[3:0] <= emwr_ctrlmode_reg[3:0];
emwr_datamode[1:0] <= emwr_datamode_reg[1:0];
end // else: !if(~s_axi_aresetn)
//###################################################
//#READ REQUEST (DATA CHANNEL)
//###################################################
// ------------------------------------------
// -- read data handler
// -- reads are performed by sending a read
// -- request out the tx port and waiting for
// -- data to come back through the rx port.
// --
// -- because elink reads are not generally
// -- returned in order, we will only allow
// -- one at a time. that's ok because reads
// -- are to be avoided for speed anyway.
// ------------------------------------------
// since we're only sending one req at a time we can ignore the fifo flags
// always read response data immediately
assign emrr_rd_en = emrr_access & ~mi_rd_reg; //TODO: Verify this assumption!!!
assign emrq_write = 1'b0;
assign emrq_data[31:0] = 32'b0;
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
emrq_access <= 1'b0;
emrq_datamode[1:0] <= 2'd0;
emrq_ctrlmode[3:0] <= 4'd0;
emrq_dstaddr[31:0] <= 32'd0;
emrq_srcaddr[31:0] <= 32'd0;
ractive_reg <= 1'b0;
rnext <= 1'b0;
end
else
begin
ractive_reg <= read_active; //read request state machone
rnext <= s_axi_rvalid & s_axi_rready & ~s_axi_rlast;
emrq_access <= ( ~ractive_reg & read_active ) | rnext;
emrq_datamode[1:0] <= axi_arsize[1:0];
emrq_ctrlmode[3:0] <= ecfg_tx_ctrlmode;
emrq_dstaddr[31:0] <= axi_araddr[31:0];
emrq_srcaddr[31:0] <= {c_read_tag_addr[11:0], 20'd0};//TODO? What can we do with lower 32 bits?
end
//Read response AXI state machine
always @( posedge s_axi_aclk )
if ( s_axi_aresetn == 1'b0 )
begin
s_axi_rvalid <= 1'b0;
s_axi_rdata[31:0] <= 32'd0;
s_axi_rresp <= 2'd0;
end
else
begin
if( emrr_access | mi_rd_reg )
begin
s_axi_rvalid <= 1'b1;
s_axi_rresp <= 2'd0;
case( axi_arsize[1:0] )
2'b00: s_axi_rdata[31:0] <= {4{emrr_mux_data[7:0]}}; //8-bit
2'b01: s_axi_rdata[31:0] <= {2{emrr_mux_data[15:0]}}; //16-bit
default: s_axi_rdata[31:0] <= emrr_mux_data[31:0]; //32-bit
endcase // case ( axi_arsize[1:0] )
end
else if( s_axi_rready )
s_axi_rvalid <= 1'b0;
end // else: !if( s_axi_aresetn == 1'b0 )
//###################################################
//#Register Inteface Logic
//###################################################
assign mi_clk = s_axi_aclk;
//Register file access (from slave)
assign mi_wr = emwr_access & (emwr_dstaddr[31:20]==ELINKID);
assign mi_rd = emrq_access & (emrq_dstaddr[31:20]==ELINKID);
//Only 32 bit writes supported
assign mi_we = mi_wr;
assign mi_en = mi_wr | mi_rd;
//Read/write address
assign mi_addr[19:0] = mi_we ? emwr_dstaddr[19:0] :
emrq_dstaddr[19:0];
//Block select
assign mi_ecfg_sel = mi_en & (mi_addr[19:16]==`EGROUP_MMR);
assign mi_rx_emmu_sel = mi_en & (mi_addr[19:16]==`EGROUP_RXMMU);
assign mi_tx_emmu_sel = mi_en & (mi_addr[19:16]==`EGROUP_TXMMU);
assign mi_embox_sel = mi_en & (mi_addr[19:16]==`EGROUP_EMBOX);
//Data
assign mi_din[31:0] = emwr_data[31:0];
//Readback
always@ (posedge mi_clk)
begin
mi_ecfg_reg <= mi_ecfg_sel;
mi_rx_emmu_reg <= mi_rx_emmu_sel;
mi_tx_emmu_reg <= mi_tx_emmu_sel;
mi_embox_reg <= mi_embox_sel;
mi_rd_reg <= mi_rd;
end
//Data mux
assign mi_dout[31:0] = mi_ecfg_reg ? mi_ecfg_dout[31:0] :
mi_rx_emmu_reg ? mi_rx_emmu_dout[31:0] :
mi_tx_emmu_reg ? mi_tx_emmu_dout[31:0] :
mi_embox_dout[31:0];
/********************************/
/*INTERFACE TO AXI SLAVE */
/********************************/
//Read Response
assign emrr_mux_data[31:0] = mi_rd_reg ? mi_dout[31:0] :
emrr_data[31:0];
endmodule