1
0
mirror of https://github.com/aolofsson/oh.git synced 2025-01-17 20:02:53 +08:00
oh/axi/hdl/esaxi.v
2020-01-28 18:12:57 -05:00

523 lines
17 KiB
Verilog

module esaxi (/*autoarg*/
// Outputs
wr_access, wr_packet, rd_access, rd_packet, rr_wait, s_axi_arready,
s_axi_awready, s_axi_bid, s_axi_bresp, s_axi_bvalid, s_axi_rid,
s_axi_rdata, s_axi_rlast, s_axi_rresp, s_axi_rvalid, s_axi_wready,
// Inputs
wr_wait, rd_wait, rr_access, rr_packet, s_axi_aclk, s_axi_aresetn,
s_axi_arid, s_axi_araddr, s_axi_arburst, s_axi_arcache,
s_axi_arlock, s_axi_arlen, s_axi_arprot, s_axi_arqos, s_axi_arsize,
s_axi_arvalid, s_axi_awid, s_axi_awaddr, s_axi_awburst,
s_axi_awcache, s_axi_awlock, s_axi_awlen, s_axi_awprot,
s_axi_awqos, s_axi_awsize, s_axi_awvalid, s_axi_bready,
s_axi_rready, s_axi_wid, s_axi_wdata, s_axi_wlast, s_axi_wstrb,
s_axi_wvalid
);
parameter S_IDW = 12;
parameter PW = 104;
parameter [AW-1:0] RETURN_ADDR = 0;
parameter AW = 32;
parameter DW = 32;
`ifdef TARGET_SIM
parameter TW = 16; //timeout counter width
`else
parameter TW = 16; //timeout counter width
`endif
//#############################
//# Write request
//#############################
output wr_access;
output [PW-1:0] wr_packet;
input wr_wait;
//#############################
//# Read request
//#############################
output rd_access;
output [PW-1:0] rd_packet;
input rd_wait;
//#############################
//# Read response
//#############################
input rr_access;
input [PW-1:0] rr_packet;
output rr_wait;
//#############################
//# AXI Slave Interface
//#############################
//Clock and reset
input s_axi_aclk;
input s_axi_aresetn;
//Read address channel
input [S_IDW-1:0] s_axi_arid; //write address ID
input [31:0] s_axi_araddr;
input [1:0] s_axi_arburst;
input [3:0] s_axi_arcache;
input s_axi_arlock;
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 [S_IDW-1:0] s_axi_awid; //write address ID
input [31:0] s_axi_awaddr;
input [1:0] s_axi_awburst;
input [3:0] s_axi_awcache;
input s_axi_awlock;
input [7:0] s_axi_awlen;
input [2:0] s_axi_awprot;
input [3:0] s_axi_awqos;
input [2:0] s_axi_awsize;
input s_axi_awvalid;
output s_axi_awready;
//Buffered write response channel
output [S_IDW-1:0] s_axi_bid; //write address ID
output [1:0] s_axi_bresp;
output s_axi_bvalid;
input s_axi_bready;
//Read channel
output [S_IDW-1:0] s_axi_rid; //write address ID
output [31:0] s_axi_rdata;
output s_axi_rlast;
output [1:0] s_axi_rresp;
output s_axi_rvalid;
input s_axi_rready;
//Write channel
input [S_IDW-1:0] s_axi_wid; //write address ID
input [31:0] s_axi_wdata;
input s_axi_wlast;
input [3:0] s_axi_wstrb;
input s_axi_wvalid;
output s_axi_wready;
//###################################################
//#WIRE/REG DECLARATIONS
//###################################################
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] axi_awaddr; // 32b for epiphany addr
reg [1:0] axi_awburst;
reg [2:0] axi_awsize;
reg [S_IDW-1:0] axi_bid; //what to do with this?
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 [S_IDW-1:0] s_axi_rid;
reg read_active;
reg [31:0] read_addr;
reg write_active;
reg b_wait; // waiting to issue write response (unlikely?)
reg wr_access;
reg [1:0] wr_datamode;
reg [31:0] wr_dstaddr;
reg [31:0] wr_data;
reg [31:0] wr_data_reg;
reg [31:0] wr_dstaddr_reg;
reg [1:0] wr_datamode_reg;
reg rd_access;
reg [1:0] rd_datamode;
reg [31:0] rd_dstaddr;
reg [31:0] rd_srcaddr; //read reaspne address
reg pre_wr_en; // delay for data alignment
reg ractive_reg; // need leading edge of active for 1st req
reg rnext;
wire last_wr_beat;
wire last_rd_beat;
wire [31:0] rr_mux_data;
wire [DW-1:0] rr_data;
wire [31:0] rr_return_data;
reg [TW-1:0] timeout_counter;
//###################################################
//#PACKET TO MESH
//###################################################
//WR
emesh2packet e2p_wr (
// Outputs
.packet_out (wr_packet[PW-1:0]),
// Inputs
.write_out (1'b1),
.datamode_out (wr_datamode[1:0]),
.ctrlmode_out (5'b0),
.dstaddr_out (wr_dstaddr[AW-1:0]),
.data_out (wr_data[DW-1:0]),
.srcaddr_out (32'b0)//only 32b slave write supported
);
//RD
emesh2packet e2p_rd (
// Outputs
.packet_out (rd_packet[PW-1:0]),
// Inputs
.write_out (1'b0),
.datamode_out (rd_datamode[1:0]),
.ctrlmode_out (5'b0),
.dstaddr_out (rd_dstaddr[AW-1:0]),
.data_out (32'b0),
.srcaddr_out (rd_srcaddr[AW-1:0])
);
//RR
packet2emesh p2e_rr (
// Outputs
.write_in (),
.datamode_in (),
.ctrlmode_in (),
.dstaddr_in (),
.data_in (rr_data[DW-1:0]),
.srcaddr_in (),
// Inputs
.packet_in (rr_packet[PW-1:0])
);
//###################################################
//#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'b1; //TODO: why not set default as 1?
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 )
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
axi_bid[S_IDW-1:0] <= '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
axi_bid[S_IDW-1:0] <= s_axi_awid[S_IDW-1:0];
axi_awaddr[31:0] <= s_axi_awaddr[31:0];
axi_awsize[2:0] <= s_axi_awsize[2:0]; // 0=byte, 1=16b, 2=32b
axi_awburst[1:0] <= s_axi_awburst[1:0]; // type, 0=fixed, 1=incr, 2=wrap
end
else if( s_axi_wvalid & s_axi_wready )
if( axi_awburst == 2'b01 )
begin //incremental burst
//TODOL FIX This, this is not right (double bug canceling!!)
axi_awaddr[31:2] <= axi_awaddr[31:2] + 32'd1;
axi_awaddr[1:0] <= 2'b0;
end
end // else: !if(~s_axi_aresetn)
//###################################################
//#WRITE RESPONSE CHANNEL
//###################################################
assign s_axi_bid = axi_bid;
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 <= ~wr_wait;
end
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
//###################################################
//No
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[S_IDW-1:0] <= 'd0;
end
else
begin
if( s_axi_arready & s_axi_arvalid )
begin
axi_araddr[31:0] <= s_axi_araddr[31:0]; //NOTE: upper 2 bits get chopped by Zynq
axi_arlen[7:0] <= s_axi_arlen[7:0];
axi_arburst <= s_axi_arburst;
axi_arsize <= s_axi_arsize;
s_axi_rlast <= ~(|s_axi_arlen[7:0]);
s_axi_rid[S_IDW-1:0] <= s_axi_arid[S_IDW-1:0];
end
else if( s_axi_rvalid & s_axi_rready)
begin
axi_arlen[7:0] <= axi_arlen[7:0] - 1;
if(axi_arlen[7:0] == 8'd1)
s_axi_rlast <= 1'b1;
if( s_axi_arburst == 2'b01)
begin //incremental burst
axi_araddr[31:2] <= axi_araddr[31:2] + 1;
axi_araddr[1:0] <= 2'b0;
end
end // if ( s_axi_rvalid & s_axi_rready)
end // else: !if( s_axi_aresetn == 1'b0 )
//###################################################
//#WRITE REQUEST
//###################################################
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
wr_data_reg[31:0] <= 32'd0;
wr_dstaddr_reg[31:0] <= 32'd0;
wr_datamode_reg[1:0] <= 2'd0;
wr_access <= 1'b0;
pre_wr_en <= 1'b0;
end
else
begin
pre_wr_en <= s_axi_wready & s_axi_wvalid;
wr_access <= pre_wr_en;
wr_datamode_reg[1:0] <= axi_awsize[1:0];
wr_dstaddr_reg[31:2] <= axi_awaddr[31:2]; //set lsbs of address based on write strobes
//What is up with this logic??
if(s_axi_wstrb[0])//| (axi_awsize[1:0]==2'b10)32-bits
begin
wr_data_reg[31:0] <= s_axi_wdata[31:0];
wr_dstaddr_reg[1:0] <= 2'd0;
end
else if(s_axi_wstrb[1])
begin
wr_data_reg[31:0] <= {8'd0, s_axi_wdata[31:8]};
wr_dstaddr_reg[1:0] <= 2'd1;
end
else if(s_axi_wstrb[2])
begin
wr_data_reg[31:0] <= {16'd0, s_axi_wdata[31:16]};
wr_dstaddr_reg[1:0] <= 2'd2;
end
else
begin
wr_data_reg[31:0] <= {24'd0, s_axi_wdata[31:24]};
wr_dstaddr_reg[1:0] <= 2'd3;
end
end // else: !if(~s_axi_aresetn)
//Pipeline stage!
always @( posedge s_axi_aclk )
begin
wr_data[31:0] <= wr_data_reg[31:0];
wr_dstaddr[31:0] <= wr_dstaddr_reg[31:0];
wr_datamode[1:0] <= wr_datamode_reg[1:0];
end
//###################################################
//#READ REQUEST (DATA CHANNEL)
//###################################################
// -- reads are performed by sending a read
// -- request out the tx port and waiting for
// -- data to come back through the rx read response port.
// --
// -- because elink reads are not generally
// -- returned in order, we will only allow
// -- one at a time.
//Need to look at rd_wait signal
always @( posedge s_axi_aclk )
if (~s_axi_aresetn)
begin
rd_access <= 1'b0;
rd_datamode[1:0] <= 2'd0;
rd_dstaddr[31:0] <= 32'd0;
rd_srcaddr[31:0] <= 32'd0;
ractive_reg <= 1'b0;
rnext <= 1'b0;
end
else
begin
ractive_reg <= read_active;
rnext <= s_axi_rvalid & s_axi_rready & ~s_axi_rlast;
rd_access <= ( ~ractive_reg & read_active ) | rnext;
rd_datamode[1:0] <= axi_arsize[1:0];
rd_dstaddr[31:0] <= axi_araddr[31:0];
rd_srcaddr[31:0] <= RETURN_ADDR;
//TODO: use arid+srcaddr for out of order ?
end
//###################################################
//#READ RESPONSE (DATA CHANNEL)
//###################################################
//Read response AXI state machine
//Only one outstanding read
assign rr_wait = 1'b0;
assign rr_return_access = rr_access | rr_timeout_access;
assign rr_return_data[31:0] = rr_timeout_access ? 32'hDEADBEEF :
rr_data[31:0];
always @( posedge s_axi_aclk )
if (!s_axi_aresetn)
begin
s_axi_rvalid <= 1'b0;
s_axi_rdata[31:0] <= 32'd0;
s_axi_rresp <= 2'd0;
end
else
begin
if( rr_return_access )
begin
s_axi_rvalid <= 1'b1;
s_axi_rresp <= rr_timeout_access ? 2'b10 : 2'b00;
case( axi_arsize[1:0] )
2'b00: s_axi_rdata[31:0] <= {4{rr_return_data[7:0]}}; //8-bit
2'b01: s_axi_rdata[31:0] <= {2{rr_return_data[15:0]}}; //16-bit
default: s_axi_rdata[31:0] <= rr_return_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 )
//###################################################
//#TIMEOUT CIRCUIT
//###################################################
reg [1:0] timeout_state;
`define TIMEOUT_IDLE 2'b00
`define TIMEOUT_ARMED 2'b01
`define TIMEOUT_EXPIRED 2'b10
always @ (posedge s_axi_aclk)
if(!s_axi_aresetn)
timeout_state[1:0] <= `TIMEOUT_IDLE;
else
case(timeout_state[1:0])
`TIMEOUT_IDLE : timeout_state[1:0] <= (s_axi_arvalid & s_axi_arready ) ? `TIMEOUT_ARMED :
`TIMEOUT_IDLE;
`TIMEOUT_ARMED : timeout_state[1:0] <= rr_access ? `TIMEOUT_IDLE :
counter_expired ? `TIMEOUT_EXPIRED :
`TIMEOUT_ARMED;
`TIMEOUT_EXPIRED : timeout_state[1:0] <= `TIMEOUT_IDLE;
default : timeout_state[1:0] <= `TIMEOUT_IDLE;
endcase // case (timeout_state[1:0])
//release bus after 64K clock cycles (seems reasonable?)
always @ (posedge s_axi_aclk)
if(timeout_state[1:0]==`TIMEOUT_IDLE)
timeout_counter[TW-1:0] <= {(TW){1'b1}};
else if (timeout_state[1:0]==`TIMEOUT_ARMED) //decrement while counter > 0
timeout_counter[TW-1:0] <= timeout_counter[TW-1:0] - 1'b1;
assign counter_expired = ~(|timeout_counter[TW-1:0]);
assign rr_timeout_access = (timeout_state[1:0]==`TIMEOUT_EXPIRED);
endmodule // esaxi