mirror of
https://github.com/parallella/parallella-hw.git
synced 2024-11-24 11:35:00 +00:00
378 lines
12 KiB
Verilog
378 lines
12 KiB
Verilog
/*
|
|
File: axi_slave_rd.v
|
|
|
|
This file is part of the Parallella FPGA Reference Design.
|
|
|
|
Copyright (C) 2013 Adapteva, Inc.
|
|
Contributed by Roman Trogan <support@adapteva.com>
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program (see the file COPYING). If not, see
|
|
<http://www.gnu.org/licenses/>.
|
|
*/
|
|
//# Limitations:
|
|
//# 1. Read burst cannot cross single core boundaries
|
|
|
|
module axi_slave_rd (/*AUTOARG*/
|
|
// Outputs
|
|
arready, rid, rdata, rresp, rlast, rvalid, emesh_access_inb,
|
|
emesh_write_inb, emesh_datamode_inb, emesh_ctrlmode_inb,
|
|
emesh_dstaddr_inb, emesh_srcaddr_inb, emesh_data_inb,
|
|
emesh_wr_wait_inb,
|
|
// Inputs
|
|
aclk, eclk, reset, arid, araddr, arlen, arsize, arburst, arlock,
|
|
arcache, arprot, arvalid, rready, emesh_access_outb,
|
|
emesh_write_outb, emesh_datamode_outb, emesh_ctrlmode_outb,
|
|
emesh_dstaddr_outb, emesh_srcaddr_outb, emesh_data_outb,
|
|
emesh_rd_wait_outb
|
|
);
|
|
|
|
parameter SIDW = 12; //ID Width
|
|
parameter SAW = 32; //Address Bus Width
|
|
parameter SDW = 32; //Data Bus Width
|
|
parameter ACH = SAW+SIDW+5; //Width of all used Read Address Signals
|
|
parameter DFW = 4; //Data channel Fifo address width
|
|
parameter DCH = SDW+SIDW+1; //Width of all used Read Data Signals
|
|
|
|
//#########
|
|
//# Inputs
|
|
//#########
|
|
|
|
// global signals
|
|
input aclk; // clock source of the axi bus
|
|
input eclk; // clock source of emesh interface
|
|
input reset; // reset
|
|
|
|
//########################
|
|
//# Read address channel
|
|
//########################
|
|
input [SIDW-1:0] arid; //read address ID
|
|
input [SAW-1:0] araddr; //read address
|
|
input [3:0] arlen; //burst lenght (the number of data transfers)
|
|
input [2:0] arsize; //burst size (the size of each transfer)
|
|
input [1:0] arburst; //burst type
|
|
input [1:0] arlock; //lock type (atomic characteristics)
|
|
input [3:0] arcache; //memory type
|
|
input [2:0] arprot; //protection type
|
|
input arvalid; //write address valid
|
|
|
|
//########################
|
|
//# Read data channel
|
|
//########################
|
|
input rready; //read ready
|
|
|
|
//##############################
|
|
//# From the emesh interface
|
|
//##############################
|
|
input emesh_access_outb;
|
|
input emesh_write_outb;
|
|
input [1:0] emesh_datamode_outb;
|
|
input [3:0] emesh_ctrlmode_outb;
|
|
input [31:0] emesh_dstaddr_outb;
|
|
input [31:0] emesh_srcaddr_outb;
|
|
input [31:0] emesh_data_outb;
|
|
input emesh_rd_wait_outb;
|
|
|
|
//##########
|
|
//# Outputs
|
|
//##########
|
|
|
|
//########################
|
|
//# Read address channel
|
|
//########################
|
|
output arready;//read address ready
|
|
|
|
//########################
|
|
//# Read data channel
|
|
//########################
|
|
output [SIDW-1:0] rid; //read ID tag (must match arid of the transaction)
|
|
output [SDW-1:0] rdata;//read data
|
|
output [1:0] rresp; //read response
|
|
output rlast; //read last, indicates the last transfer in burst
|
|
output rvalid;//read valid
|
|
|
|
//##############################
|
|
//# To the emesh interface
|
|
//##############################
|
|
output emesh_access_inb;
|
|
output emesh_write_inb;
|
|
output [1:0] emesh_datamode_inb;
|
|
output [3:0] emesh_ctrlmode_inb;
|
|
output [31:0] emesh_dstaddr_inb;
|
|
output [31:0] emesh_srcaddr_inb;
|
|
output [31:0] emesh_data_inb;
|
|
output emesh_wr_wait_inb;
|
|
|
|
/*AUTOINPUT*/
|
|
/*AUTOWIRE*/
|
|
|
|
//#########
|
|
//# Regs
|
|
//#########
|
|
reg [3:0] tran_len;
|
|
reg tran_go_reg;
|
|
reg [SIDW+5:0] tran_info_reg;
|
|
reg tran_last_reg;
|
|
|
|
reg [31:0] dstaddr_reg;
|
|
reg [31:0] data_reg;
|
|
reg emesh_wr_access_reg;
|
|
reg [31:0] realgn_byte;
|
|
reg dch_fifo_empty_reg;
|
|
reg [DCH-1:0] dch_fifo_reg;
|
|
|
|
//#########
|
|
//# Wires
|
|
//#########
|
|
wire arready;
|
|
|
|
wire tran_go;
|
|
wire tran_stall;
|
|
wire tran_last_int;
|
|
wire tran_last;//Indicates last data of burst (out of fifo_dch)
|
|
wire [SAW-1:0] tran_addr;
|
|
wire [1:0] tran_mode;
|
|
wire [SIDW-1:0] arid_ec;
|
|
wire [3:0] arlen_ec;
|
|
wire [3:0] arlen_new;
|
|
wire arlen_dec;
|
|
wire [3:0] arlen_upd;
|
|
wire rd_last;
|
|
wire [2:0] dalgn_ctrl;
|
|
wire byte_tran;
|
|
wire hword_tran;
|
|
wire word_tran;
|
|
wire [SIDW+5:0] tran_info;
|
|
|
|
wire emesh_wr_access;
|
|
wire dch_fifo_full;
|
|
wire [2:0] realgn_ctrl;
|
|
wire [31:0] realgn_hword;
|
|
wire byte_realgn;
|
|
wire hword_realgn;
|
|
wire word_realgn;
|
|
wire [31:0] data_realgn;
|
|
wire [SIDW-1:0] tran_id;
|
|
wire last_tran;
|
|
wire [DCH-1:0] dch_fifo_in;
|
|
wire dch_fifo_wr;
|
|
wire dch_fifo_rd;
|
|
wire dch_fifo_empty;
|
|
wire [DCH-1:0] dch_fifo_out;
|
|
wire rvalid_rready;
|
|
wire dch_advance;
|
|
wire new_burst;
|
|
|
|
//#######################################
|
|
//# Address channel synchronization FIFO
|
|
//#######################################
|
|
|
|
/*axi_slave_addrch AUTO_TEMPLATE(.aclk (aclk),
|
|
.addr (araddr[]),
|
|
.ach_fifo_empty (),
|
|
.a\(.*\) (ar\1[]),
|
|
.new_addr_sel (new_burst),
|
|
);
|
|
*/
|
|
|
|
axi_slave_addrch axi_slave_addrch(/*AUTOINST*/
|
|
// Outputs
|
|
.aready (arready), // Templated
|
|
.ach_fifo_empty (), // Templated
|
|
.tran_addr (tran_addr[SAW-1:0]),
|
|
.tran_mode (tran_mode[1:0]),
|
|
.dalgn_ctrl (dalgn_ctrl[2:0]),
|
|
.byte_tran (byte_tran),
|
|
.hword_tran (hword_tran),
|
|
.word_tran (word_tran),
|
|
.aid_ec (arid_ec[SIDW-1:0]), // Templated
|
|
.alen_ec (arlen_ec[3:0]), // Templated
|
|
.new_addr_sel (new_burst), // Templated
|
|
// Inputs
|
|
.aclk (aclk), // Templated
|
|
.eclk (eclk),
|
|
.reset (reset),
|
|
.avalid (arvalid), // Templated
|
|
.addr (araddr[SAW-1:0]), // Templated
|
|
.aid (arid[SIDW-1:0]), // Templated
|
|
.alen (arlen[3:0]), // Templated
|
|
.asize (arsize[2:0]), // Templated
|
|
.aburst (arburst[1:0]), // Templated
|
|
.tran_last (tran_last),
|
|
.tran_stall (tran_stall),
|
|
.tran_go (tran_go));
|
|
|
|
//########################
|
|
//# AXI-EMESH conversion
|
|
//########################
|
|
|
|
assign tran_go = |(arlen_new[3:0]);
|
|
assign tran_last_int = dch_fifo_wr & last_tran;
|
|
assign tran_last = tran_last_reg & ~tran_stall;
|
|
|
|
always @ (posedge eclk or posedge reset)
|
|
if(reset)
|
|
tran_last_reg <= 1'b0;
|
|
else if(tran_last_int | ~tran_stall)
|
|
tran_last_reg <= tran_last_int;
|
|
|
|
always @ (posedge eclk or posedge reset)
|
|
if(reset)
|
|
tran_len[3:0] <= 4'b0000;
|
|
else if(tran_go & ~tran_stall)
|
|
tran_len[3:0] <= arlen_new[3:0];
|
|
|
|
always @ (posedge eclk or posedge reset)
|
|
if(reset)
|
|
tran_go_reg <= 1'b0;
|
|
else if(~tran_stall)
|
|
tran_go_reg <= tran_go;
|
|
|
|
assign arlen_dec = |(tran_len[3:0]);
|
|
|
|
assign arlen_upd[3:0] = {(4){arlen_dec}} & (tran_len[3:0] - 4'b0001);
|
|
|
|
assign arlen_new[3:0] = new_burst ? arlen_ec[3:0] : arlen_upd[3:0];
|
|
|
|
assign rd_last = (tran_len[3:0] == 4'b0001);
|
|
|
|
assign tran_info[SIDW+5:0] = {arid_ec[SIDW-1:0],dalgn_ctrl[2:0],byte_tran,
|
|
hword_tran,word_tran};
|
|
|
|
always @ (posedge eclk)
|
|
if(tran_go & ~tran_stall)
|
|
tran_info_reg[SIDW+5:0] <= tran_info[SIDW+5:0];
|
|
|
|
//#############################
|
|
//# Emesh transaction creation
|
|
//#############################
|
|
|
|
assign emesh_dstaddr_inb[31:0] = tran_addr[SAW-1:0];
|
|
|
|
assign emesh_srcaddr_inb[31:0] = {`AXI_COORD,{(13-SIDW){1'b0}},
|
|
tran_info_reg[SIDW+5:0], rd_last};
|
|
assign emesh_data_inb[31:0] = 32'h00000000;
|
|
assign emesh_datamode_inb[1:0] = tran_mode[1:0];
|
|
assign emesh_ctrlmode_inb[3:0] = 4'b0000;
|
|
assign emesh_write_inb = 1'b0;
|
|
assign emesh_access_inb = tran_go_reg & ~tran_stall;
|
|
|
|
//#######################################
|
|
//# Data channel synchronization FIFO
|
|
//#######################################
|
|
|
|
assign emesh_wr_wait_inb = dch_fifo_full;
|
|
|
|
//# Incoming transaction should be sampled to prevent timing issues
|
|
assign emesh_wr_access = emesh_access_outb & emesh_write_outb &
|
|
~emesh_wr_wait_inb;
|
|
|
|
always @ (posedge eclk)
|
|
if (emesh_wr_access)
|
|
dstaddr_reg[31:0] <= emesh_dstaddr_outb[31:0];
|
|
|
|
always @ (posedge eclk)
|
|
if (emesh_wr_access)
|
|
data_reg[31:0] <= emesh_data_outb[31:0];
|
|
|
|
always @ (posedge eclk or posedge reset)
|
|
if(reset)
|
|
emesh_wr_access_reg <= 1'b0;
|
|
else if(~emesh_wr_wait_inb)
|
|
emesh_wr_access_reg <= emesh_wr_access;
|
|
|
|
//# RID
|
|
assign tran_id[SIDW-1:0] = dstaddr_reg[SIDW+6:7];
|
|
|
|
//# Data Re-alignment from the EMESH protocol
|
|
assign realgn_ctrl[2:0] = dstaddr_reg[6:4];
|
|
assign byte_realgn = dstaddr_reg[3];
|
|
assign hword_realgn = dstaddr_reg[2];
|
|
assign word_realgn = dstaddr_reg[1];
|
|
|
|
//# Last transfer
|
|
assign last_tran = dstaddr_reg[0];
|
|
|
|
always @ (realgn_ctrl[1:0] or data_reg[7:0])
|
|
begin
|
|
casez (realgn_ctrl[1:0])
|
|
2'b00 : realgn_byte[31:0] = {{(24){1'b0}},data_reg[7:0] };
|
|
2'b01 : realgn_byte[31:0] = {{(16){1'b0}},data_reg[7:0],{( 8){1'b0}}};
|
|
2'b10 : realgn_byte[31:0] = {{(8){1'b0}},data_reg[7:0],{(16){1'b0}}};
|
|
2'b11 : realgn_byte[31:0] = {data_reg[7:0],{(24){1'b0}}};
|
|
default: realgn_byte[31:0] = {{(24){1'b0}},data_reg[7:0]};
|
|
endcase // casez (realgn_ctrl[1:0])
|
|
end
|
|
|
|
assign realgn_hword[31:0] = realgn_ctrl[1] ? {data_reg[15:0],{(16){1'b0}}} :
|
|
{{(16){1'b0}},data_reg[15:0]};
|
|
|
|
assign data_realgn[31:0] = byte_realgn ? realgn_byte[31:0] :
|
|
hword_realgn ? realgn_hword[31:0]:
|
|
data_reg[31:0];
|
|
|
|
|
|
assign dch_fifo_in[DCH-1:0] = {data_realgn[31:0],tran_id[SIDW-1:0],last_tran};
|
|
assign dch_fifo_wr = emesh_wr_access_reg & ~dch_fifo_full;
|
|
assign dch_fifo_rd = ~dch_fifo_empty & (~rvalid | rvalid_rready);
|
|
assign dch_advance = rvalid_rready | ~rvalid;
|
|
|
|
/*fifo AUTO_TEMPLATE(.rd_clk (aclk),
|
|
.wr_clk (eclk),
|
|
.wr_data (dch_fifo_in[DCH-1:0]),
|
|
.rd_data (dch_fifo_out[DCH-1:0]),
|
|
.rd_fifo_empty (dch_fifo_empty),
|
|
.wr_fifo_full (dch_fifo_full),
|
|
.wr_write (dch_fifo_wr),
|
|
.rd_read (dch_fifo_rd),
|
|
);
|
|
*/
|
|
|
|
fifo #(.DW(DCH), .AW(DFW)) fifo_dch(/*AUTOINST*/
|
|
// Outputs
|
|
.rd_data (dch_fifo_out[DCH-1:0]), // Templated
|
|
.rd_fifo_empty (dch_fifo_empty), // Templated
|
|
.wr_fifo_full (dch_fifo_full), // Templated
|
|
// Inputs
|
|
.reset (reset),
|
|
.wr_clk (eclk), // Templated
|
|
.rd_clk (aclk), // Templated
|
|
.wr_write (dch_fifo_wr), // Templated
|
|
.wr_data (dch_fifo_in[DCH-1:0]), // Templated
|
|
.rd_read (dch_fifo_rd)); // Templated
|
|
|
|
|
|
//# The data is sampled after exiting FIFO to prevent timing issues
|
|
always @ (posedge aclk or posedge reset)
|
|
if(reset)
|
|
dch_fifo_empty_reg <= 1'b1;
|
|
else if(dch_advance)
|
|
dch_fifo_empty_reg <= dch_fifo_empty;
|
|
|
|
always @ (posedge aclk)
|
|
if (dch_advance)
|
|
dch_fifo_reg[DCH-1:0] <= dch_fifo_out[DCH-1:0];
|
|
|
|
assign rid[SIDW-1:0] = dch_fifo_reg[SIDW:1];
|
|
assign rresp[1:0] = 2'b00;
|
|
assign rvalid = ~dch_fifo_empty_reg;
|
|
assign rvalid_rready = rvalid & rready;
|
|
assign rdata[SDW-1:0] = dch_fifo_reg[DCH-1:SIDW+1];
|
|
assign rlast = dch_fifo_reg[0];
|
|
|
|
//# Transaction Stall
|
|
assign tran_stall = emesh_rd_wait_outb;
|
|
|
|
|
|
endmodule // axi_slave_rd
|