1
0
mirror of https://github.com/KastnerRG/riffa.git synced 2025-01-30 23:02:54 +08:00
riffa/fpga/riffa_hdl/tx_port_writer.v

511 lines
18 KiB
Verilog

// ----------------------------------------------------------------------
// Copyright (c) 2015, The Regents of the University of California All
// rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
//
// * Neither the name of The Regents of the University of California
// nor the names of its contributors may be used to endorse or
// promote products derived from this software without specific
// prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL REGENTS OF THE
// UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.
// ----------------------------------------------------------------------
//----------------------------------------------------------------------------
// Filename: tx_port_writer.v
// Version: 1.00.a
// Verilog Standard: Verilog-2001
// Description: Handles receiving new transaction events and data, and
// making requests to tx engine.
// for the RIFFA channel.
// Author: Matt Jacobsen
// History: @mattj: Version 2.0
//-----------------------------------------------------------------------------
`define S_TXPORTWR_MAIN_IDLE 8'b0000_0001
`define S_TXPORTWR_MAIN_CHECK 8'b0000_0010
`define S_TXPORTWR_MAIN_SIG_NEW 8'b0000_0100
`define S_TXPORTWR_MAIN_NEW_ACK 8'b0000_1000
`define S_TXPORTWR_MAIN_WRITE 8'b0001_0000
`define S_TXPORTWR_MAIN_DONE 8'b0010_0000
`define S_TXPORTWR_MAIN_SIG_DONE 8'b0100_0000
`define S_TXPORTWR_MAIN_RESET 8'b1000_0000
`define S_TXPORTWR_TX_IDLE 8'b0000_0001
`define S_TXPORTWR_TX_BUF 8'b0000_0010
`define S_TXPORTWR_TX_ADJ_0 8'b0000_0100
`define S_TXPORTWR_TX_ADJ_1 8'b0000_1000
`define S_TXPORTWR_TX_ADJ_2 8'b0001_0000
`define S_TXPORTWR_TX_CHECK_DATA 8'b0010_0000
`define S_TXPORTWR_TX_WRITE 8'b0100_0000
`define S_TXPORTWR_TX_WRITE_REM 8'b1000_0000
`timescale 1ns/1ns
module tx_port_writer (
input CLK,
input RST,
input [2:0] CONFIG_MAX_PAYLOAD_SIZE, // Maximum write payload: 000=128B, 001=256B, 010=512B, 011=1024B
output TXN, // Write transaction notification
input TXN_ACK, // Write transaction acknowledged
output [31:0] TXN_LEN, // Write transaction length
output [31:0] TXN_OFF_LAST, // Write transaction offset/last
output [31:0] TXN_DONE_LEN, // Write transaction actual transfer length
output TXN_DONE, // Write transaction done
output TXN_ERR, // Write transaction encountered an error
input TXN_DONE_ACK, // Write transaction actual transfer length read
input NEW_TXN, // Transaction parameters are valid
output NEW_TXN_ACK, // Transaction parameter read, continue
input NEW_TXN_LAST, // Channel last write
input [31:0] NEW_TXN_LEN, // Channel write length (in 32 bit words)
input [30:0] NEW_TXN_OFF, // Channel write offset
input [31:0] NEW_TXN_WORDS_RECVD, // Count of data words received in transaction
input NEW_TXN_DONE, // Transaction is closed
input [63:0] SG_ELEM_ADDR, // Scatter gather element address
input [31:0] SG_ELEM_LEN, // Scatter gather element length (in words)
input SG_ELEM_RDY, // Scatter gather element ready
input SG_ELEM_EMPTY, // Scatter gather elements empty
output SG_ELEM_REN, // Scatter gather element read enable
output SG_RST, // Scatter gather data reset
input SG_ERR, // Scatter gather read encountered an error
output TX_REQ, // Outgoing write request
input TX_REQ_ACK, // Outgoing write request acknowledged
output [63:0] TX_ADDR, // Outgoing write high address
output [9:0] TX_LEN, // Outgoing write length (in 32 bit words)
output TX_LAST, // Outgoing write is last request for transaction
input TX_SENT // Outgoing write complete
);
`include "functions.vh"
(* syn_encoding = "user" *)
(* fsm_encoding = "user" *)
reg [7:0] rMainState=`S_TXPORTWR_MAIN_IDLE, _rMainState=`S_TXPORTWR_MAIN_IDLE;
reg [31:0] rOffLast=0, _rOffLast=0;
reg rWordsEQ0=0, _rWordsEQ0=0;
reg rStarted=0, _rStarted=0;
reg [31:0] rDoneLen=0, _rDoneLen=0;
reg rSgErr=0, _rSgErr=0;
reg rTxErrd=0, _rTxErrd=0;
reg rTxnAck=0, _rTxnAck=0;
(* syn_encoding = "user" *)
(* fsm_encoding = "user" *)
reg [7:0] rTxState=`S_TXPORTWR_TX_IDLE, _rTxState=`S_TXPORTWR_TX_IDLE;
reg [31:0] rSentWords=0, _rSentWords=0;
reg [31:0] rWords=0, _rWords=0;
reg [31:0] rBufWords=0, _rBufWords=0;
reg [31:0] rBufWordsInit=0, _rBufWordsInit=0;
reg rLargeBuf=0, _rLargeBuf=0;
reg [63:0] rAddr=64'd0, _rAddr=64'd0;
reg [2:0] rCarry=0, _rCarry=0;
reg rValsPropagated=0, _rValsPropagated=0;
reg [5:0] rValsProp=0, _rValsProp=0;
reg rCopyBufWords=0, _rCopyBufWords=0;
reg rUseInit=0, _rUseInit=0;
reg [10:0] rPageRem=0, _rPageRem=0;
reg rPageSpill=0, _rPageSpill=0;
reg rPageSpillInit=0, _rPageSpillInit=0;
reg [10:0] rPreLen=0, _rPreLen=0;
reg [2:0] rMaxPayloadSize=0, _rMaxPayloadSize=0;
reg [2:0] rMaxPayloadShift=0, _rMaxPayloadShift=0;
reg [9:0] rMaxPayload=0, _rMaxPayload=0;
reg rPayloadSpill=0, _rPayloadSpill=0;
reg rMaxLen=1, _rMaxLen=1;
reg [9:0] rLen=0, _rLen=0;
reg [31:0] rSendingWords=0, _rSendingWords=0;
reg rAvail=0, _rAvail=0;
reg [1:0] rTxnDone=0, _rTxnDone=0;
reg [9:0] rLastLen=0, _rLastLen=0;
reg rLastLenEQ0=0, _rLastLenEQ0=0;
reg rLenEQWords=0, _rLenEQWords=0;
reg rLenEQBufWords=0, _rLenEQBufWords=0;
reg rNotRequesting=1, _rNotRequesting=1;
reg [63:0] rReqAddr=64'd0, _rReqAddr=64'd0;
reg [9:0] rReqLen=0, _rReqLen=0;
reg rReqLast=0, _rReqLast=0;
reg rTxReqAck=0, _rTxReqAck=0;
reg rDone=0, _rDone=0;
reg [9:0] rAckCount=0, _rAckCount=0;
reg rTxSent=0, _rTxSent=0;
reg rLastDoneRead=1, _rLastDoneRead=1;
reg rTxnDoneAck=0, _rTxnDoneAck=0;
reg rReqPartialDone=0, _rReqPartialDone=0;
reg rPartialDone=0, _rPartialDone=0;
assign NEW_TXN_ACK = rMainState[1]; // S_TXPORTWR_MAIN_CHECK
assign TXN = rMainState[2]; // S_TXPORTWR_MAIN_SIG_NEW
assign TXN_DONE = (rMainState[6] | rPartialDone); // S_TXPORTWR_MAIN_SIG_DONE
assign TXN_LEN = rWords;
assign TXN_OFF_LAST = rOffLast;
assign TXN_DONE_LEN = rDoneLen;
assign TXN_ERR = rTxErrd;
assign SG_ELEM_REN = rTxState[2]; // S_TXPORTWR_TX_ADJ_0
assign SG_RST = rMainState[3]; // S_TXPORTWR_MAIN_NEW_ACK
assign TX_REQ = !rNotRequesting;
assign TX_ADDR = rReqAddr;
assign TX_LEN = rReqLen;
assign TX_LAST = rReqLast;
// Buffer the input signals that come from outside the tx_port.
always @ (posedge CLK) begin
rTxnAck <= #1 (RST ? 1'd0 : _rTxnAck);
rTxnDoneAck <= #1 (RST ? 1'd0 : _rTxnDoneAck);
rSgErr <= #1 (RST ? 1'd0 : _rSgErr);
rTxReqAck <= #1 (RST ? 1'd0 : _rTxReqAck);
rTxSent <= #1 (RST ? 1'd0 : _rTxSent);
end
always @ (*) begin
_rTxnAck = TXN_ACK;
_rTxnDoneAck = TXN_DONE_ACK;
_rSgErr = SG_ERR;
_rTxReqAck = TX_REQ_ACK;
_rTxSent = TX_SENT;
end
// Wait for a NEW_TXN request. Then request transfers until all the data is sent
// or until the specified length is reached. Then signal TXN_DONE.
always @ (posedge CLK) begin
rMainState <= #1 (RST ? `S_TXPORTWR_MAIN_IDLE : _rMainState);
rOffLast <= #1 _rOffLast;
rWordsEQ0 <= #1 _rWordsEQ0;
rStarted <= #1 _rStarted;
rDoneLen <= #1 (RST ? 0 : _rDoneLen);
rTxErrd <= #1 (RST ? 1'd0 : _rTxErrd);
end
always @ (*) begin
_rMainState = rMainState;
_rOffLast = rOffLast;
_rWordsEQ0 = rWordsEQ0;
_rStarted = rStarted;
_rDoneLen = rDoneLen;
_rTxErrd = rTxErrd;
case (rMainState)
`S_TXPORTWR_MAIN_IDLE: begin // Wait for channel write request
_rStarted = 0;
_rWordsEQ0 = (NEW_TXN_LEN == 0);
_rOffLast = {NEW_TXN_OFF, NEW_TXN_LAST};
if (NEW_TXN)
_rMainState = `S_TXPORTWR_MAIN_CHECK;
end
`S_TXPORTWR_MAIN_CHECK: begin // Continue with transaction?
if (rOffLast[0] | !rWordsEQ0)
_rMainState = `S_TXPORTWR_MAIN_SIG_NEW;
else
_rMainState = `S_TXPORTWR_MAIN_RESET;
end
`S_TXPORTWR_MAIN_SIG_NEW: begin // Signal new write
_rMainState = `S_TXPORTWR_MAIN_NEW_ACK;
end
`S_TXPORTWR_MAIN_NEW_ACK: begin // Wait for acknowledgement
if (rTxnAck) // ACK'd on PC read of TXN length
_rMainState = (rWordsEQ0 ? `S_TXPORTWR_MAIN_SIG_DONE : `S_TXPORTWR_MAIN_WRITE);
end
`S_TXPORTWR_MAIN_WRITE: begin // Start writing and wait for all writes to complete
_rStarted = (rStarted | rTxState[1]); // S_TXPORTWR_TX_BUF
_rTxErrd = (rTxErrd | rSgErr);
if (rTxState[0] & rStarted) // S_TXPORTWR_TX_IDLE
_rMainState = `S_TXPORTWR_MAIN_DONE;
end
`S_TXPORTWR_MAIN_DONE: begin // Wait for the last transaction to complete
if (rDone & rLastDoneRead) begin
_rDoneLen = rSentWords;
_rMainState = `S_TXPORTWR_MAIN_SIG_DONE;
end
end
`S_TXPORTWR_MAIN_SIG_DONE: begin // Signal the done port
_rTxErrd = 0;
_rMainState = `S_TXPORTWR_MAIN_RESET;
end
`S_TXPORTWR_MAIN_RESET: begin // Wait for the channel tx to drop
if (NEW_TXN_DONE)
_rMainState = `S_TXPORTWR_MAIN_IDLE;
end
default: begin
_rMainState = `S_TXPORTWR_MAIN_IDLE;
end
endcase
end
// Manage sending TX requests to the TX engine. Transfers will be limited
// by each scatter gather buffer's size, max payload size, and must not
// cross a (4KB) page boundary. The request is only made if there is sufficient
// data already written to the buffer.
wire [9:0] wLastLen = (NEW_TXN_WORDS_RECVD - rSentWords);
wire [9:0] wAddrLoInv = ~rAddr[11:2];
wire [10:0] wPageRem = (wAddrLoInv + 1'd1);
always @ (posedge CLK) begin
rTxState <= #1 (RST | rSgErr ? `S_TXPORTWR_TX_IDLE : _rTxState);
rSentWords <= #1 (rMainState[0] ? 0 : _rSentWords);
rWords <= #1 _rWords;
rBufWords <= #1 _rBufWords;
rBufWordsInit <= #1 _rBufWordsInit;
rAddr <= #1 _rAddr;
rCarry <= #1 _rCarry;
rValsPropagated <= #1 _rValsPropagated;
rValsProp <= #1 _rValsProp;
rLargeBuf <= #1 _rLargeBuf;
rPageRem <= #1 _rPageRem;
rPageSpill <= #1 _rPageSpill;
rPageSpillInit <= #1 _rPageSpillInit;
rCopyBufWords <= #1 _rCopyBufWords;
rUseInit <= #1 _rUseInit;
rPreLen <= #1 _rPreLen;
rMaxPayloadSize <= #1 _rMaxPayloadSize;
rMaxPayloadShift <= #1 _rMaxPayloadShift;
rMaxPayload <= #1 _rMaxPayload;
rPayloadSpill <= #1 _rPayloadSpill;
rMaxLen <= #1 (RST ? 1'd1 : _rMaxLen);
rLen <= #1 _rLen;
rSendingWords <= #1 _rSendingWords;
rAvail <= #1 _rAvail;
rTxnDone <= #1 _rTxnDone;
rLastLen <= #1 _rLastLen;
rLastLenEQ0 <= #1 _rLastLenEQ0;
rLenEQWords <= #1 _rLenEQWords;
rLenEQBufWords <= #1 _rLenEQBufWords;
end
always @ (*) begin
_rTxState = rTxState;
_rCopyBufWords = rCopyBufWords;
_rUseInit = rUseInit;
_rValsProp = ((rValsProp<<1) | rTxState[3]); // S_TXPORTWR_TX_ADJ_1
_rValsPropagated = (rValsProp == 6'd0);
_rLargeBuf = (SG_ELEM_LEN > rWords);
{_rCarry[0], _rAddr[15:0]} = (rTxState[1] ? SG_ELEM_ADDR[15:0] : (rAddr[15:0] + ({12{rTxState[6]}} & {rLen, 2'd0}))); // S_TXPORTWR_TX_WRITE
{_rCarry[1], _rAddr[31:16]} = (rTxState[1] ? SG_ELEM_ADDR[31:16] : (rAddr[31:16] + rCarry[0]));
{_rCarry[2], _rAddr[47:32]} = (rTxState[1] ? SG_ELEM_ADDR[47:32] : (rAddr[47:32] + rCarry[1]));
_rAddr[63:48] = (rTxState[1] ? SG_ELEM_ADDR[63:48] : (rAddr[63:48] + rCarry[2]));
_rSentWords = (rTxState[7] ? NEW_TXN_WORDS_RECVD : rSentWords) + ({10{rTxState[6]}} & rLen); // S_TXPORTWR_TX_WRITE
_rWords = (NEW_TXN_ACK ? NEW_TXN_LEN : (rWords - ({10{rTxState[6]}} & rLen))); // S_TXPORTWR_TX_WRITE
_rBufWordsInit = (rLargeBuf ? rWords : SG_ELEM_LEN);
_rBufWords = (rCopyBufWords ? rBufWordsInit : rBufWords) - ({10{rTxState[6]}} & rLen); // S_TXPORTWR_TX_WRITE
_rPageRem = wPageRem;
_rPageSpillInit = (rBufWordsInit > wPageRem);
_rPageSpill = (rBufWords > wPageRem);
_rPreLen = ((rPageSpillInit & rUseInit) | (rPageSpill & !rUseInit) ? rPageRem : rBufWords[10:0]);
_rMaxPayloadSize = CONFIG_MAX_PAYLOAD_SIZE;
_rMaxPayloadShift = (rMaxPayloadSize > 3'd4 ? 3'd4 : rMaxPayloadSize);
_rMaxPayload = (6'd32<<rMaxPayloadShift);
_rPayloadSpill = (rPreLen > rMaxPayload);
_rMaxLen = ((rMaxLen & !rValsProp[1]) | rTxState[6]); // S_TXPORTWR_TX_WRITE
_rLen = (rPayloadSpill | rMaxLen ? rMaxPayload : rPreLen[9:0]);
_rSendingWords = rSentWords + rLen;
_rAvail = (NEW_TXN_WORDS_RECVD >= rSendingWords);
_rTxnDone = ((rTxnDone<<1) | NEW_TXN_DONE);
_rLastLen = wLastLen;
_rLastLenEQ0 = (rLastLen == 10'd0);
_rLenEQWords = (rLen == rWords);
_rLenEQBufWords = (rLen == rBufWords);
case (rTxState)
`S_TXPORTWR_TX_IDLE: begin // Wait for channel write request
if (rMainState[4] & !rStarted) // S_TXPORTWR_MAIN_WRITE
_rTxState = `S_TXPORTWR_TX_BUF;
end
`S_TXPORTWR_TX_BUF: begin // Wait for buffer length and address
if (SG_ELEM_RDY)
_rTxState = `S_TXPORTWR_TX_ADJ_0;
end
`S_TXPORTWR_TX_ADJ_0: begin // Fix for large buffer
_rCopyBufWords = 1;
_rTxState = `S_TXPORTWR_TX_ADJ_1;
end
`S_TXPORTWR_TX_ADJ_1: begin // Check for page boundary crossing
_rCopyBufWords = 0;
_rUseInit = rCopyBufWords;
_rTxState = `S_TXPORTWR_TX_ADJ_2;
end
`S_TXPORTWR_TX_ADJ_2: begin // Wait for values to propagate
// Fix for page boundary crossing
// Check for max payload
// Fix for max payload
_rUseInit = 0;
if (rValsProp[2])
_rTxState = `S_TXPORTWR_TX_CHECK_DATA;
end
`S_TXPORTWR_TX_CHECK_DATA: begin // Wait for available data
if (rNotRequesting) begin
if (rAvail)
_rTxState = `S_TXPORTWR_TX_WRITE;
else if (rValsPropagated & rTxnDone[1])
_rTxState = (rLastLenEQ0 ? `S_TXPORTWR_TX_IDLE : `S_TXPORTWR_TX_WRITE_REM);
end
end
`S_TXPORTWR_TX_WRITE: begin // Send len and repeat or finish?
if (rLenEQWords)
_rTxState = `S_TXPORTWR_TX_IDLE;
else if (rLenEQBufWords)
_rTxState = `S_TXPORTWR_TX_BUF;
else
_rTxState = `S_TXPORTWR_TX_ADJ_1;
end
`S_TXPORTWR_TX_WRITE_REM: begin // Send remaining data and finish
_rTxState = `S_TXPORTWR_TX_IDLE;
end
default: begin
_rTxState = `S_TXPORTWR_TX_IDLE;
end
endcase
end
// Request TX transfers separately so that the TX FSM can continue calculating
// the next set of request parameters without having to wait for the TX_REQ_ACK.
always @ (posedge CLK) begin
rAckCount <= #1 (RST ? 10'd0 : _rAckCount);
rNotRequesting <= #1 (RST ? 1'd1 : _rNotRequesting);
rReqAddr <= #1 _rReqAddr;
rReqLen <= #1 _rReqLen;
rReqLast <= #1 _rReqLast;
rDone <= #1 _rDone;
rLastDoneRead <= #1 (RST ? 1'd1 : _rLastDoneRead);
end
always @ (*) begin
// Start signaling when the TX FSM is ready.
if (rTxState[6] | rTxState[7]) // S_TXPORTWR_TX_WRITE
_rNotRequesting = 0;
else
_rNotRequesting = (rNotRequesting | rTxReqAck);
// Pass off the rAddr & rLen when ready and wait for TX_REQ_ACK.
if (rTxState[6]) begin // S_TXPORTWR_TX_WRITE
_rReqAddr = rAddr;
_rReqLen = rLen;
_rReqLast = rLenEQWords;
end
else if (rTxState[7]) begin // S_TXPORTWR_TX_WRITE_REM
_rReqAddr = rAddr;
_rReqLen = rLastLen;
_rReqLast = 1;
end
else begin
_rReqAddr = rReqAddr;
_rReqLen = rReqLen;
_rReqLast = rReqLast;
end
// Track TX_REQ_ACK and TX_SENT to determine when the transaction is over.
_rDone = (rAckCount == 10'd0);
if (rMainState[0]) // S_TXPORTWR_MAIN_IDLE
_rAckCount = 0;
else
_rAckCount = rAckCount + rTxState[6] + rTxState[7] - rTxSent; // S_TXPORTWR_TX_WRITE, S_TXPORTWR_TX_WRITE_REM
// Track when the user reads the actual transfer amount.
_rLastDoneRead = (rMainState[6] ? 1'd0 : (rLastDoneRead | rTxnDoneAck)); // S_TXPORTWR_MAIN_SIG_DONE
end
// Facilitate sending a TXN_DONE when we receive a TXN_ACK after the transaction
// has begun sending. This will happen when the workstation detects that it has
// sent/used all its currently mapped scatter gather elements, but it's not enough
// to complete the transaction. The TXN_DONE will let the workstation know it can
// release the current scatter gather mappings and allocate new ones.
always @ (posedge CLK) begin
rPartialDone <= #1 _rPartialDone;
rReqPartialDone <= #1 (RST ? 1'd0 : _rReqPartialDone);
end
always @ (*) begin
// Signal TXN_DONE after we've recieved the (seemingly superfluous) TXN_ACK,
// we have no outstanding transfer requests, we're not currently requesting a
// transfer, and there are no more scatter gather elements.
_rPartialDone = (rReqPartialDone & rDone & rNotRequesting & SG_ELEM_EMPTY & rTxState[1]); // S_TXPORTWR_TX_BUF
// Keep track of (seemingly superfluous) TXN_ACK requests.
if ((rReqPartialDone & rDone & rNotRequesting & SG_ELEM_EMPTY & rTxState[1]) | rMainState[0]) // S_TXPORTWR_MAIN_IDLE
_rReqPartialDone = 0;
else
_rReqPartialDone = (rReqPartialDone | (rTxnAck & !rMainState[3])); // !S_TXPORTWR_MAIN_NEW_ACK
end
/*
wire [35:0] wControl0;
chipscope_icon_1 cs_icon(
.CONTROL0(wControl0)
);
chipscope_ila_t8_512 a0(
.CLK(CLK),
.CONTROL(wControl0),
.TRIG0({rTxState[6] | rTxState[7] | rTxSent, rAckCount[6:0]}),
.DATA({280'd0,
NEW_TXN_WORDS_RECVD, // 32
rSendingWords, // 32
rAvail, // 1
rNotRequesting, // 1
NEW_TXN_LAST, // 1
NEW_TXN_LEN, // 32
NEW_TXN_OFF, // 31
NEW_TXN, // 1
rAckCount, // 10
rLastDoneRead, // 1
rWords, // 32
rBufWords, // 32
rLen, // 10
rTxState, // 8
rMainState}) // 8
);
*/
endmodule