Commit 0091c623 authored by Grzegorz Daniluk's avatar Grzegorz Daniluk

add wrsw_nic simulation model

parent 1cb9d610
`define ADDR_NIC_CR 9'h0
`define NIC_CR_RX_EN_OFFSET 0
`define NIC_CR_RX_EN 32'h00000001
`define NIC_CR_TX_EN_OFFSET 1
`define NIC_CR_TX_EN 32'h00000002
`define NIC_CR_SW_RST_OFFSET 31
`define NIC_CR_SW_RST 32'h80000000
`define ADDR_NIC_SR 9'h4
`define NIC_SR_BNA_OFFSET 0
`define NIC_SR_BNA 32'h00000001
`define NIC_SR_REC_OFFSET 1
`define NIC_SR_REC 32'h00000002
`define NIC_SR_TX_DONE_OFFSET 2
`define NIC_SR_TX_DONE 32'h00000004
`define NIC_SR_TX_ERROR_OFFSET 3
`define NIC_SR_TX_ERROR 32'h00000008
`define NIC_SR_CUR_TX_DESC_OFFSET 8
`define NIC_SR_CUR_TX_DESC 32'h00000700
`define NIC_SR_CUR_RX_DESC_OFFSET 16
`define NIC_SR_CUR_RX_DESC 32'h00070000
`define ADDR_NIC_EIC_IDR 9'h20
`define NIC_EIC_IDR_RCOMP_OFFSET 0
`define NIC_EIC_IDR_RCOMP 32'h00000001
`define NIC_EIC_IDR_TCOMP_OFFSET 1
`define NIC_EIC_IDR_TCOMP 32'h00000002
`define NIC_EIC_IDR_TXERR_OFFSET 2
`define NIC_EIC_IDR_TXERR 32'h00000004
`define ADDR_NIC_EIC_IER 9'h24
`define NIC_EIC_IER_RCOMP_OFFSET 0
`define NIC_EIC_IER_RCOMP 32'h00000001
`define NIC_EIC_IER_TCOMP_OFFSET 1
`define NIC_EIC_IER_TCOMP 32'h00000002
`define NIC_EIC_IER_TXERR_OFFSET 2
`define NIC_EIC_IER_TXERR 32'h00000004
`define ADDR_NIC_EIC_IMR 9'h28
`define NIC_EIC_IMR_RCOMP_OFFSET 0
`define NIC_EIC_IMR_RCOMP 32'h00000001
`define NIC_EIC_IMR_TCOMP_OFFSET 1
`define NIC_EIC_IMR_TCOMP 32'h00000002
`define NIC_EIC_IMR_TXERR_OFFSET 2
`define NIC_EIC_IMR_TXERR 32'h00000004
`define ADDR_NIC_EIC_ISR 9'h2c
`define NIC_EIC_ISR_RCOMP_OFFSET 0
`define NIC_EIC_ISR_RCOMP 32'h00000001
`define NIC_EIC_ISR_TCOMP_OFFSET 1
`define NIC_EIC_ISR_TCOMP 32'h00000002
`define NIC_EIC_ISR_TXERR_OFFSET 2
`define NIC_EIC_ISR_TXERR 32'h00000004
`define BASE_NIC_DTX 9'h80
`define SIZE_NIC_DTX 32'h20
`define BASE_NIC_DRX 9'h100
`define SIZE_NIC_DRX 32'h20
`ifndef __SIMDRV_WR_NIC_SVH
`define __SIMDRV_WR_NIC_SVH 1
`timescale 1ns/1ps
`include "simdrv_defs.svh"
`include "eth_packet.svh"
`include "wb_packet_source.svh"
`include "wb_packet_sink.svh"
`include "regs/nic_regs.vh"
`define MAX_PACKET_SIZE 1536
// fake, simulated spinlock
typedef struct {
int locked;
} spinlock_t;
// RX and TX descriptor definitions
typedef struct {
bit empty;
bit error;
bit[5:0] port_id;
bit got_ts;
bit[27:0] ts_r;
bit[3:0] ts_f;
bit[15:0] length;
bit[15:0] offset;
} nic_rx_descriptor_t ;
typedef struct {
bit ready;
bit error;
bit pad_e;
bit ts_e;
bit[15:0] ts_id;
bit[31:0] dpm;
bit[15:0] length;
bit[15:0] offset;
} nic_tx_descriptor_t ;
`define NUM_RX_DESC 8
`define NUM_TX_DESC 8
`define PACKET_QUEUE_SIZE 64
`define BASE_NIC_MEM 'h8000
class CSimDrv_NIC;
bit little_endian;
int tx_oob_fid;
int rx_head_idx;
int tx_head_idx;
int tx_irq_enabled;
int tx_queue_active = 0;
protected uint16_t untag_tab[256];
spinlock_t tx_lock;
EthPacket rx_queue[$], tx_queue[$];
CBusAccessor acc_regs;
uint64_t base_addr;
function new(CBusAccessor regs_, uint64_t base_addr_, bit little_endian_=1);
base_addr = base_addr_;
acc_regs = regs_;
little_endian = little_endian_;
endfunction // new
function automatic logic[31:0] swap_endian(input [31:0] data);
if(little_endian)
return {data[7:0], data[15:8], data[23:16], data[31:24]};
else
return data;
endfunction // swap_endian
task writel(uint32_t addr, uint32_t val);
acc_regs.write(base_addr + addr, val, 4);
endtask // writel
task readl(uint32_t addr, output uint32_t val);
uint64_t tmp;
acc_regs.read(base_addr + addr, tmp, 4);
val = tmp;
endtask // readl
task automatic enable_rx();
bit[31:0] tmp;
readl(`ADDR_NIC_CR, tmp);
writel(`ADDR_NIC_CR, tmp | `NIC_CR_RX_EN);
writel(`ADDR_NIC_EIC_IER, `NIC_EIC_IER_RCOMP); // enable RX interrupt
endtask // automatic
task automatic enable_tx();
bit[31:0] tmp;
tx_irq_enabled = 1;
readl(`ADDR_NIC_CR, tmp);
writel(`ADDR_NIC_CR, tmp | `NIC_CR_TX_EN);
writel(`ADDR_NIC_EIC_IER, `NIC_EIC_IER_TCOMP | `NIC_EIC_IER_TXERR); // enable TXCOMP & TXERR interrupts
endtask // automatic
task automatic disable_tx();
bit[31:0] tmp;
readl(`ADDR_NIC_CR, tmp);
tmp = tmp & ~(`NIC_CR_TX_EN);
writel(`ADDR_NIC_CR, tmp);
writel(`ADDR_NIC_EIC_IDR, `NIC_EIC_IER_TCOMP | `NIC_EIC_IER_TXERR); // enable TXCOMP & TXERR interrupts
tx_irq_enabled = 0;
endtask // automatic
task automatic disable_rx();
bit[31:0] tmp;
readl(`ADDR_NIC_CR, tmp);
tmp = tmp & (~`NIC_CR_RX_EN);
writel(`ADDR_NIC_CR, tmp);
writel(`ADDR_NIC_EIC_IDR, `NIC_EIC_IER_RCOMP); // disable RX interrupt
endtask // automatic
task automatic write_rx_desc(int idx, nic_rx_descriptor_t desc);
// IF RX is enabled, make sure we're not chaging the address/length of an active (empty) descriptor.
writel(`BASE_NIC_DRX + (idx * 16 + 8), (desc.length << 16) | desc.offset);
writel(`BASE_NIC_DRX + (idx * 16), { 30'h0 , 1'b0, desc.empty} );
endtask // write_rx_desc
task automatic write_tx_desc(int idx, nic_tx_descriptor_t desc);
// IF RX is enabled, make sure we're not chaging the address/length of an active (empty) descriptor.
writel(`BASE_NIC_DTX + (idx * 16 + 8), (desc.dpm));
writel(`BASE_NIC_DTX + (idx * 16 + 4), (desc.length << 16) | desc.offset);
writel(`BASE_NIC_DTX + (idx * 16), { desc.ts_id, 12'h0, desc.pad_e, desc.ts_e , 1'b0, desc.ready} );
endtask // write_tx_desc
task automatic read_rx_desc(int idx, output nic_rx_descriptor_t desc);
bit [31:0] tmp;
// $display("IDx %d\n", idx);
readl(`BASE_NIC_DRX + (idx * 16 + 0), tmp);
// $display("r0 %x", tmp);
desc.port_id = tmp[13:8];
desc.got_ts = tmp[14];
desc.error = tmp[1];
desc.empty = tmp[0];
readl(`BASE_NIC_DRX + (idx * 16 + 4), tmp);
// $display("r1 %x", tmp);
desc.ts_f = tmp[31:28];
desc.ts_r = tmp[27:0];
readl(`BASE_NIC_DRX + (idx * 16 + 8), tmp);
// $display("r2 %x", tmp);
desc.length = tmp[31:16];
desc.offset = tmp[15:0];
endtask // write_rx_desc
task automatic read_tx_desc(int idx, output nic_tx_descriptor_t desc);
bit[31:0] tmp;
readl(`BASE_NIC_DTX + (idx * 16 + 0), tmp);
desc.ts_id = tmp[31:16];
desc.pad_e = tmp[3];
desc.ts_e = tmp[2];
desc.error = tmp[1];
desc.ready = tmp[0];
readl(`BASE_NIC_DTX + (idx * 16 + 4), tmp);
desc.length = tmp[31:16];
desc.offset = tmp[15:0];
readl(`BASE_NIC_DTX + (idx * 16 + 8), tmp);
desc.dpm = tmp;
endtask // read_tx_desc
task automatic create_rx_desc(int idx, int offset, int length);
nic_rx_descriptor_t desc;
desc.offset = offset;
desc.length = length;
desc.empty = 1;
desc.error = 0;
desc.got_ts = 0;
write_rx_desc(idx, desc);
endtask // automatic
task automatic create_tx_desc(int idx, int offset, int length, int ts_e);
nic_tx_descriptor_t desc;
desc.offset = offset;
desc.length = length;
desc.ready = 1;
desc.pad_e = (length < 59) ? 1: 0;
desc.ts_e = ts_e;
desc.ts_id = (ts_e ? tx_oob_fid ++ : 0);
desc.dpm = 32'hffffffff;
write_tx_desc(idx, desc);
endtask // automatic
int count;
task automatic nic_hw_rx(nic_rx_descriptor_t desc);
EthPacket pkt;
int i, n;
string s;
u64_array_t pbuff;
byte_array_t payload, p2;
pbuff = new[2048];
count++;
// $display("Cnt %d [dsize %d]", count, desc.length);
for(i=0; i<(desc.length+8)/4 ; i++)
begin
bit [31:0] tmp;
readl(`BASE_NIC_MEM + desc.offset + i * 4, tmp);
tmp = swap_endian(tmp);
pbuff[i] = tmp;
end
pkt = new;
payload = SimUtils.unpack(pbuff, 4, desc.length + 2);
p2 = new[desc.length];
for (i=0;i<desc.length;i++)
p2[i] = payload[i+2];
pkt.deserialize(p2);
pkt.error = desc.error;
rx_queue.push_back(pkt);
endtask // automatic
task spin_lock_init(inout spinlock_t lck);
lck.locked =0;
endtask // spinlock_init
task spin_lock(inout spinlock_t lck);
while(lck.locked) #1ns;
lck.locked =1;
endtask // spin_lock
task spin_unlock(inout spinlock_t lck);
lck.locked =0;
endtask // spin_unlock
task automatic nic_hw_tx(int idx, EthPacket pkt);
nic_tx_descriptor_t desc;
reg[31:0] tmp;
u64_array_t pbuf;
byte payload[];
int i;
pkt.serialize(payload);
desc.offset = 'h4000 + idx * 'h800;
desc.length = payload.size();
desc.pad_e = (desc.length < 60 ? 1 : 0);
desc.ts_e = (pkt.oob == TX_FID ? 1: 0);
desc.dpm = 32'hffffffff;
desc.ts_id = tx_oob_fid++;
desc.ready = 1;
desc.error = 0;
pbuf = SimUtils.pack({0,0,payload}, 4, 1);
for(i=0;i<pbuf.size(); i++)
writel(`BASE_NIC_MEM + desc.offset + i * 4, swap_endian(pbuf[i]));
write_tx_desc(idx, desc);
endtask // automatic
task automatic nic_start_xmit(EthPacket pkt, output ok);
//FIXME: check if there are any free descriptors
reg[31:0] rval;
nic_tx_descriptor_t desc;
spin_lock(tx_lock); // make sure the interrupt handler won't make a mess here
read_tx_desc(tx_head_idx, desc);
if(desc.ready) // the head descriptor still hasn't been transmitted? Perhaps the NIC is still transmitting it.
begin
$display("nic_start_xmit: no free tx descriptors");
spin_unlock(tx_lock);
ok = 0;
return;
end
nic_hw_tx(tx_head_idx, pkt);
tx_head_idx++;
if(tx_head_idx == `NUM_TX_DESC)
tx_head_idx = 0;
spin_unlock(tx_lock);
enable_tx();
ok =1;
endtask // nic_start_xmit
task automatic handle_rcomp_irq();
nic_rx_descriptor_t desc;
int n_read;
n_read = 0;
forever begin
read_rx_desc(rx_head_idx, desc);
if(desc.empty /* || n_read == `NUM_RX_DESC */)
break;
// $display("Offset: %x len: %d error: %d port_id %d ts_r %d ts_f %d got_ts %d",desc.offset, desc.length, desc.error, desc.port_id, desc.ts_r, desc.ts_f, desc.got_ts);
nic_hw_rx(desc);
create_rx_desc(rx_head_idx, 'h800*rx_head_idx, 1600);
rx_head_idx++;
if(rx_head_idx == `NUM_RX_DESC)
rx_head_idx = 0;
n_read++;
end
writel(`ADDR_NIC_EIC_ISR, `NIC_EIC_ISR_RCOMP);
endtask // automatic
task automatic handle_tcomp_irq();
nic_tx_descriptor_t desc;
EthPacket pkt;
spin_lock(tx_lock);
if(!tx_queue.size())
begin
disable_tx(); // disable TX irq
tx_head_idx = 0;
writel(`ADDR_NIC_EIC_ISR, `NIC_EIC_ISR_TCOMP);
spin_unlock(tx_lock);
return;
end
while(tx_queue.size() > 0)
begin
read_tx_desc(tx_head_idx, desc);
if(desc.ready || desc.error) // the head descriptor still hasn't been transmitted? Perhaps the NIC is still transmitting it.
begin
$display("handle_tcomp_irq: no free TX descriptors at the moment");
break;
end
pkt = tx_queue.pop_front();
nic_hw_tx(tx_head_idx, pkt);
tx_head_idx++;
if(tx_head_idx == `NUM_TX_DESC)
tx_head_idx = 0;
end // while (tx_queue.get_count())
writel(`ADDR_NIC_EIC_ISR, `NIC_EIC_ISR_TCOMP);
spin_unlock(tx_lock);
endtask // handle_tcomp_irq
task automatic handle_txerr_irq();
nic_tx_descriptor_t desc;
int cur_tx_desc;
reg[31:0] tmp;
readl(`ADDR_NIC_SR, tmp);
cur_tx_desc = (tmp & `NIC_SR_CUR_TX_DESC) >> `NIC_SR_CUR_TX_DESC_OFFSET;
read_tx_desc(cur_tx_desc, desc);
$display("TXerror: faulty descriptor %d error %b ready %b offset %x len %d", cur_tx_desc,desc.error, desc.ready, desc.offset, desc.length);
desc.error = 0;
desc.ready = 1; // just clear the error and try to retransmit
write_tx_desc(cur_tx_desc, desc);
writel(`ADDR_NIC_EIC_ISR, `NIC_EIC_ISR_TXERR);
// $stop;
endtask // automatic
task automatic nic_irq_handler();
reg[31:0] isr;
readl(`ADDR_NIC_EIC_ISR, isr);
// $display("irq_handler: isr %x", isr[2:0]);
if(isr & `NIC_EIC_ISR_RCOMP)
handle_rcomp_irq();
if(isr & `NIC_EIC_ISR_TXERR)
handle_txerr_irq();
if(isr & `NIC_EIC_ISR_TCOMP)
handle_tcomp_irq();
endtask // automatic
task automatic init();
int i;
disable_rx();
disable_tx();
spin_lock_init(tx_lock);
for(i=0; i<`NUM_RX_DESC * 4; i++)
begin
writel(`BASE_NIC_DRX + i *4, 0); // clear the descriptor tables
writel(`BASE_NIC_DTX + i *4, 0); // clear the descriptor tables
end
for(i=0; i<`NUM_RX_DESC; i++)
create_rx_desc(i, 'h800*i, 1600); // create NUM_RX_DESC empty RX descriptors
rx_head_idx = 0;
tx_head_idx = 0;
tx_oob_fid = 100;
tx_queue_active = 1;
enable_rx();
endtask
task update(bit nic_irq);
if(nic_irq) begin
nic_irq_handler();
#50ns;
end
if(tx_queue_active && tx_queue.size() > 0 && !tx_irq_enabled) begin
reg ok;
EthPacket pkt;
pkt = tx_queue.pop_front();
nic_start_xmit(pkt, ok);
end
endtask // update
endclass // CSimDrv_NIC
class NICPacketSource extends EthPacketSource;
CSimDrv_NIC nic;
function new (ref CSimDrv_NIC nic_);
nic = nic_;
endfunction // new
task send(ref EthPacket pkt, ref int result = _null);
nic.tx_queue.push_back(pkt);
endtask // send
endclass
class NICPacketSink extends EthPacketSink;
CSimDrv_NIC nic;
function new (ref CSimDrv_NIC nic_);
nic = nic_;
endfunction // new
function int poll();
return (nic.rx_queue.size() > 0) ? 1 :0;
endfunction // poll
//ML stuff
function int permanent_stall_enable();
//empty
return 0;
endfunction
//ML stuff
function int permanent_stall_disable();
// empty
return 0;
endfunction
task recv(ref EthPacket pkt, ref int result = _null);
while(!nic.rx_queue.size()) #1ns;
pkt = nic.rx_queue.pop_front();
endtask
endclass // NICPacketSink
`endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment