├── .gitignore ├── Makefrag ├── build.sbt └── src └── main ├── resources ├── csrc │ ├── SimNetwork.cc │ ├── TraceROM.cc │ ├── device.cc │ ├── device.h │ ├── packet.h │ ├── switch.cc │ └── switch.h └── vsrc │ ├── SimNetwork.v │ └── TraceROM.v └── scala ├── Aligner.scala ├── Buffer.scala ├── Checksum.scala ├── Configs.scala ├── Consts.scala ├── DMA.scala ├── Headers.scala ├── Limiter.scala ├── NIC.scala ├── NICTests.scala ├── Pauser.scala ├── Stream.scala ├── TCAM.scala ├── Tap.scala ├── TestUtils.scala └── TraceROM.scala /.gitignore: -------------------------------------------------------------------------------- 1 | /lib 2 | target 3 | *.so 4 | *.o 5 | -------------------------------------------------------------------------------- /Makefrag: -------------------------------------------------------------------------------- 1 | icenet_vsrcs = \ 2 | $(icenet_dir)/vsrc/SimNetwork.v \ 3 | $(icenet_dir)/vsrc/TraceROM.v 4 | 5 | icenet_csrcs = \ 6 | $(icenet_dir)/csrc/SimNetwork.cc \ 7 | $(icenet_dir)/csrc/device.cc \ 8 | $(icenet_dir)/csrc/switch.cc \ 9 | $(icenet_dir)/csrc/TraceROM.cc 10 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | organization := "edu.berkeley.cs" 2 | 3 | version := "1.0" 4 | 5 | name := "icenet" 6 | 7 | scalaVersion := "2.13.10" 8 | -------------------------------------------------------------------------------- /src/main/resources/csrc/SimNetwork.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "device.h" 6 | #include "switch.h" 7 | 8 | class NetworkSwitch *netsw = NULL; 9 | class NetworkDevice *netdev = NULL; 10 | 11 | static inline int euclid(int a, int b) 12 | { 13 | while (b > 0) { 14 | int t = b; 15 | b = a % b; 16 | a = t; 17 | } 18 | return a; 19 | } 20 | 21 | extern "C" void network_init( 22 | const char *devname, 23 | int rlimit_gbps, 24 | char *rlimit_inc, 25 | char *rlimit_period) 26 | { 27 | int inc = rlimit_gbps, period = 64; 28 | int gcd = euclid(inc, period); 29 | 30 | *rlimit_inc = inc / gcd; 31 | *rlimit_period = (period / gcd) - 1; 32 | 33 | netsw = new NetworkSwitch(devname); 34 | netdev = new NetworkDevice(random_macaddr()); 35 | 36 | netsw->add_device(netdev); 37 | } 38 | 39 | extern "C" void network_tick( 40 | unsigned char out_valid, 41 | long long out_data, 42 | unsigned char out_last, 43 | 44 | unsigned char *in_valid, 45 | long long *in_data, 46 | unsigned char *in_last, 47 | 48 | long long *macaddr) 49 | { 50 | if (!netdev || !netsw) { 51 | fprintf(stderr, "You forgot to call network_init!"); 52 | exit(1); 53 | } 54 | 55 | netdev->tick(out_valid, out_data, out_last); 56 | netdev->switch_to_host(); 57 | 58 | netsw->distribute(); 59 | netsw->switch_to_worker(); 60 | 61 | *in_valid = netdev->in_valid(); 62 | *in_data = netdev->in_data(); 63 | *in_last = netdev->in_last(); 64 | *macaddr = netdev->macaddr(); 65 | } 66 | -------------------------------------------------------------------------------- /src/main/resources/csrc/TraceROM.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct flit { 8 | uint64_t data; 9 | uint8_t keep; 10 | int last; 11 | }; 12 | 13 | std::queue flits; 14 | 15 | extern "C" int trace_rom_init(const char *fname) 16 | { 17 | FILE *f; 18 | flit flt; 19 | 20 | f = fopen(fname, "r"); 21 | 22 | while (!feof(f)) { 23 | if (fscanf(f, "%lx %x %d\n", &flt.data, &flt.keep, &flt.last) != 3) { 24 | perror("fscanf()"); 25 | abort(); 26 | } 27 | flits.push(flt); 28 | } 29 | 30 | return flits.size(); 31 | } 32 | 33 | extern "C" void trace_rom_tick( 34 | char *stream_valid, 35 | char stream_ready, 36 | long *stream_data, 37 | char *stream_keep, 38 | char *stream_last) 39 | { 40 | if (!flits.empty() && stream_ready) { 41 | flits.pop(); 42 | } 43 | 44 | if (flits.empty()) { 45 | *stream_valid = 0; 46 | *stream_data = 0; 47 | *stream_keep = 0; 48 | *stream_last = 0; 49 | } else { 50 | *stream_valid = 1; 51 | *stream_data = flits.front().data; 52 | *stream_keep = flits.front().keep; 53 | *stream_last = flits.front().last; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/csrc/device.cc: -------------------------------------------------------------------------------- 1 | #include "device.h" 2 | 3 | #include 4 | 5 | void NetworkDevice::host_thread(void *arg) 6 | { 7 | NetworkDevice *netdev = static_cast(arg); 8 | netdev->run(); 9 | 10 | while (true) 11 | netdev->target->switch_to(); 12 | } 13 | 14 | NetworkDevice::NetworkDevice(uint64_t macaddr) 15 | { 16 | _macaddr = macaddr; 17 | target = context_t::current(); 18 | host.init(host_thread, this); 19 | } 20 | 21 | NetworkDevice::~NetworkDevice() 22 | { 23 | } 24 | 25 | void NetworkDevice::run(void) 26 | { 27 | network_packet *send_packet = new network_packet; 28 | network_packet *recv_packet; 29 | 30 | init_network_packet(send_packet); 31 | 32 | while (true) { 33 | while (!out_flits.empty()) { 34 | assert(send_packet->len < ETH_MAX_WORDS); 35 | network_packet_add(send_packet, out_flits.front().data); 36 | if (out_flits.front().last) { 37 | out_packets.push(send_packet); 38 | send_packet = new network_packet; 39 | init_network_packet(send_packet); 40 | } 41 | out_flits.pop(); 42 | } 43 | 44 | while (!in_packets.empty()) { 45 | recv_packet = in_packets.front(); 46 | for (int i = 0; i < recv_packet->len; i++) { 47 | network_flit flt; 48 | flt.data = recv_packet->data[i]; 49 | flt.last = (i + 1) == recv_packet->len; 50 | in_flits.push(flt); 51 | } 52 | in_packets.pop(); 53 | delete recv_packet; 54 | } 55 | 56 | target->switch_to(); 57 | } 58 | } 59 | 60 | 61 | void NetworkDevice::tick( 62 | bool out_valid, 63 | uint64_t out_data, 64 | bool out_last) 65 | { 66 | if (out_valid) { 67 | struct network_flit flt; 68 | flt.data = out_data; 69 | flt.last = out_last; 70 | out_flits.push(flt); 71 | } 72 | 73 | if (in_valid()) { 74 | in_flits.pop(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/resources/csrc/device.h: -------------------------------------------------------------------------------- 1 | #ifndef __ICENET_DEVICE_H__ 2 | #define __ICENET_DEVICE_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "fesvr/context.h" 9 | #include "packet.h" 10 | 11 | class NetworkDevice { 12 | public: 13 | NetworkDevice(uint64_t macaddr); 14 | ~NetworkDevice(); 15 | 16 | void tick( 17 | bool out_valid, 18 | uint64_t out_data, 19 | bool out_last); 20 | 21 | bool in_valid() { return !in_flits.empty(); } 22 | uint64_t in_data() { return (in_valid()) ? in_flits.front().data : 0; } 23 | bool in_last() { return (in_valid()) ? in_flits.front().last : false; } 24 | void switch_to_host(void) { host.switch_to(); } 25 | void send_out(struct network_flit &flt) { out_flits.push(flt); } 26 | struct network_flit recv_in(void) { 27 | struct network_flit flt = in_flits.front(); 28 | in_flits.pop(); 29 | return flt; 30 | } 31 | uint64_t macaddr() { return _macaddr; } 32 | void set_macaddr(uint64_t macaddr) { _macaddr = macaddr; } 33 | bool has_out_packet(void) { return !out_packets.empty(); } 34 | network_packet *pop_out_packet(void) { 35 | network_packet *pkt = out_packets.front(); 36 | out_packets.pop(); 37 | return pkt; 38 | } 39 | void push_in_packet(network_packet *packet) { in_packets.push(packet); } 40 | 41 | protected: 42 | std::queue out_flits; 43 | std::queue in_flits; 44 | 45 | std::queue out_packets; 46 | std::queue in_packets; 47 | 48 | static void host_thread(void *arg); 49 | virtual void run(void); 50 | 51 | context_t* target; 52 | context_t host; 53 | 54 | uint64_t _macaddr; 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/main/resources/csrc/packet.h: -------------------------------------------------------------------------------- 1 | #ifndef __ICENET_PACKET_H__ 2 | #define __ICENET_PACKET_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define NET_IP_ALIGN 2 10 | #define ETH_MAX_WORDS 190 11 | #define ETH_MAX_BYTES 1518 12 | #define MAC_ADDR_BYTES 6 13 | #define BCAST_MAC 0xffffffffffffL 14 | 15 | struct network_flit { 16 | uint64_t data; 17 | bool last; 18 | }; 19 | 20 | struct network_packet { 21 | uint64_t data[ETH_MAX_WORDS]; 22 | int len; 23 | }; 24 | 25 | static inline void init_network_packet(struct network_packet *packet) 26 | { 27 | packet->len = 0; 28 | memset(packet->data, 0, ETH_MAX_WORDS * sizeof(uint64_t)); 29 | } 30 | 31 | static inline void network_packet_add(network_packet *packet, uint64_t data) 32 | { 33 | packet->data[packet->len] = data; 34 | packet->len++; 35 | } 36 | 37 | static inline uint64_t network_packet_dstmac(network_packet *packet) 38 | { 39 | uint64_t dstmac = 0; 40 | 41 | memcpy(&dstmac, ((char *) packet->data) + NET_IP_ALIGN, MAC_ADDR_BYTES); 42 | return dstmac; 43 | } 44 | 45 | static inline uint64_t network_packet_srcmac(network_packet *packet) 46 | { 47 | uint64_t srcmac = 0; 48 | 49 | memcpy(&srcmac, 50 | ((char *) packet->data) + NET_IP_ALIGN + MAC_ADDR_BYTES, 51 | MAC_ADDR_BYTES); 52 | 53 | return srcmac; 54 | } 55 | 56 | static inline uint16_t network_packet_ethtype(network_packet *packet) 57 | { 58 | uint16_t ethtype; 59 | 60 | memcpy(ðtype, 61 | ((char *) packet->data) + NET_IP_ALIGN + 2 * MAC_ADDR_BYTES, 62 | 2); 63 | 64 | return ethtype; 65 | } 66 | 67 | static inline network_packet *network_packet_copy(network_packet *packet) 68 | { 69 | network_packet *packet_copy = new network_packet; 70 | packet_copy->len = packet->len; 71 | memcpy(packet_copy->data, packet->data, packet->len * sizeof(uint64_t)); 72 | return packet_copy; 73 | } 74 | 75 | static inline uint64_t random_macaddr(void) 76 | { 77 | uint64_t macaddr; 78 | 79 | // Generate random MAC according to 80 | // https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/5/html/Virtualization/sect-Virtualization-Tips_and_tricks-Generating_a_new_unique_MAC_address.html 81 | srandom(time(0)); 82 | macaddr = random() & 0xffff7f; 83 | macaddr <<= 24; 84 | macaddr |= 0x3e1600; 85 | 86 | return macaddr; 87 | } 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/main/resources/csrc/switch.cc: -------------------------------------------------------------------------------- 1 | #include "switch.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | static int tuntap_alloc(const char *dev, int flags) 20 | { 21 | struct ifreq ifr; 22 | int fd, err; 23 | 24 | if ((fd = open("/dev/net/tun", O_RDWR)) < 0) { 25 | perror("open()"); 26 | return fd; 27 | } 28 | 29 | memset(&ifr, 0, sizeof(ifr)); 30 | 31 | ifr.ifr_flags = flags; 32 | strncpy(ifr.ifr_name, dev, IFNAMSIZ); 33 | 34 | if ((err = ioctl(fd, TUNSETIFF, &ifr)) < 0) { 35 | perror("ioctl()"); 36 | close(fd); 37 | return err; 38 | } 39 | 40 | return fd; 41 | } 42 | 43 | NetworkSwitch::NetworkSwitch(const char *ifname) 44 | { 45 | if (ifname == NULL || strlen(ifname) == 0) { 46 | fd = -1; 47 | goto skip_tuntap; 48 | } 49 | 50 | fd = tuntap_alloc(ifname, IFF_TAP | IFF_NO_PI); 51 | if (fd < 0) { 52 | fprintf(stderr, "Could not open tap interface\n"); 53 | abort(); 54 | } 55 | 56 | skip_tuntap: 57 | main = context_t::current(); 58 | worker.init(worker_thread, this); 59 | } 60 | 61 | NetworkSwitch::~NetworkSwitch() 62 | { 63 | if (fd != -1) 64 | close(fd); 65 | } 66 | 67 | void NetworkSwitch::worker_thread(void *arg) 68 | { 69 | NetworkSwitch *sw = static_cast(arg); 70 | sw->run(); 71 | 72 | while (true) 73 | sw->main->switch_to(); 74 | } 75 | 76 | #define ceil_div(n, d) (((n) - 1) / (d) + 1) 77 | 78 | void NetworkSwitch::run(void) 79 | { 80 | fd_set rfds, wfds; 81 | struct timeval timeout; 82 | int retval; 83 | 84 | if (fd == -1) { 85 | while (true) 86 | main->switch_to(); 87 | return; 88 | } 89 | 90 | timeout.tv_sec = 1; 91 | timeout.tv_usec = 0; 92 | 93 | FD_ZERO(&rfds); 94 | FD_ZERO(&wfds); 95 | 96 | while (true) { 97 | FD_SET(fd, &rfds); 98 | FD_SET(fd, &wfds); 99 | 100 | retval = select(fd + 1, &rfds, &wfds, NULL, &timeout); 101 | 102 | if (retval < 0) { 103 | perror("select()"); 104 | abort(); 105 | } 106 | 107 | if (retval == 0) { 108 | main->switch_to(); 109 | continue; 110 | } 111 | 112 | if (FD_ISSET(fd, &rfds)) { 113 | network_packet *recv_packet; 114 | char *recv_buffer; 115 | 116 | recv_packet = new network_packet; 117 | init_network_packet(recv_packet); 118 | recv_buffer = ((char *) recv_packet->data) + NET_IP_ALIGN; 119 | 120 | retval = read(fd, recv_buffer, ETH_MAX_BYTES); 121 | if (retval < 0) { 122 | perror("read()"); 123 | abort(); 124 | } 125 | recv_packet->len = ceil_div(retval + NET_IP_ALIGN, sizeof(uint64_t)); 126 | in_packets.push(recv_packet); 127 | } 128 | 129 | if (FD_ISSET(fd, &wfds) && !out_packets.empty()) { 130 | network_packet *send_packet; 131 | char *send_buffer; 132 | size_t nbytes; 133 | 134 | send_packet = out_packets.front(); 135 | send_buffer = ((char *) send_packet->data) + NET_IP_ALIGN; 136 | nbytes = send_packet->len * sizeof(uint64_t) - NET_IP_ALIGN; 137 | 138 | retval = write(fd, send_buffer, nbytes); 139 | if (retval < 0) { 140 | perror("write()"); 141 | abort(); 142 | } 143 | 144 | out_packets.pop(); 145 | delete send_packet; 146 | } 147 | 148 | main->switch_to(); 149 | } 150 | } 151 | 152 | void NetworkSwitch::broadcast(network_packet *packet, uint64_t skipmac) 153 | { 154 | for (auto dev : devices) { 155 | if (dev->macaddr() == skipmac) 156 | continue; 157 | network_packet *packet_copy = network_packet_copy(packet); 158 | dev->push_in_packet(packet_copy); 159 | } 160 | } 161 | 162 | int NetworkSwitch::route(network_packet *packet, uint64_t dstmac) 163 | { 164 | for (auto dev : devices) { 165 | if (dev->macaddr() == dstmac) { 166 | dev->push_in_packet(packet); 167 | return 0; 168 | } 169 | } 170 | return -1; 171 | } 172 | 173 | void NetworkSwitch::distribute(void) 174 | { 175 | while (!in_packets.empty()) { 176 | network_packet *packet = in_packets.front(); 177 | uint64_t dstmac = network_packet_dstmac(packet); 178 | 179 | if (dstmac == BCAST_MAC) { 180 | broadcast(packet, 0); 181 | delete packet; 182 | } else if (route(packet, dstmac)) { 183 | fprintf(stderr, "Dropped packet for %" PRIx64 "\n", dstmac); 184 | delete packet; 185 | } 186 | in_packets.pop(); 187 | } 188 | 189 | for (auto dev : devices) { 190 | if (dev->has_out_packet()) { 191 | network_packet *packet = dev->pop_out_packet(); 192 | uint64_t dstmac = network_packet_dstmac(packet); 193 | 194 | if (dstmac == BCAST_MAC) { 195 | broadcast(packet, dev->macaddr()); 196 | delete packet; 197 | } else if (route(packet, dstmac)) { 198 | out_packets.push(packet); 199 | } 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/resources/csrc/switch.h: -------------------------------------------------------------------------------- 1 | #ifndef __ICENET_SWITCH_H__ 2 | #define __ICENET_SWITCH_H__ 3 | 4 | #include 5 | 6 | #include "device.h" 7 | 8 | class NetworkSwitch { 9 | public: 10 | NetworkSwitch(const char *ifname); 11 | ~NetworkSwitch(); 12 | 13 | void add_device(NetworkDevice *dev) { devices.push_back(dev); } 14 | void distribute(void); 15 | void switch_to_worker(void) { worker.switch_to(); } 16 | 17 | private: 18 | int fd; 19 | std::vector devices; 20 | std::queue out_packets; 21 | std::queue in_packets; 22 | 23 | static void worker_thread(void *arg); 24 | void run(void); 25 | 26 | void broadcast(network_packet *packet, uint64_t skipmac); 27 | int route(network_packet *packet, uint64_t dstmac); 28 | 29 | context_t* main; 30 | context_t worker; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /src/main/resources/vsrc/SimNetwork.v: -------------------------------------------------------------------------------- 1 | import "DPI-C" function void network_tick 2 | ( 3 | input bit out_valid, 4 | input longint out_data, 5 | input bit out_last, 6 | 7 | output bit in_valid, 8 | output longint in_data, 9 | output bit in_last, 10 | 11 | output longint macaddr 12 | ); 13 | 14 | import "DPI-C" function void network_init( 15 | input string devname, 16 | input int rlimit_gbps, 17 | output byte rlimit_inc, 18 | output byte rlimit_period 19 | ); 20 | 21 | module SimNetwork( 22 | input clock, 23 | input reset, 24 | 25 | input net_out_valid, 26 | input [63:0] net_out_bits_data, 27 | input [7:0] net_out_bits_keep, 28 | input net_out_bits_last, 29 | 30 | output net_in_valid, 31 | output [63:0] net_in_bits_data, 32 | output [7:0] net_in_bits_keep, 33 | output net_in_bits_last, 34 | 35 | output [47:0] net_macAddr, 36 | output [7:0] net_rlimit_inc, 37 | output [7:0] net_rlimit_period, 38 | output [7:0] net_rlimit_size, 39 | 40 | output [15:0] net_pauser_threshold, 41 | output [15:0] net_pauser_quanta, 42 | output [15:0] net_pauser_refresh 43 | ); 44 | // TODO: Make this work with the pauser 45 | 46 | assign net_pauser_threshold = 16'hff; 47 | assign net_pauser_quanta = 16'hff; 48 | assign net_pauser_refresh = 16'hff; 49 | 50 | 51 | string devname = ""; 52 | int rlimit_gbps = 64; 53 | byte rlimit_inc = 1; 54 | byte rlimit_period = 1; 55 | byte rlimit_size = 8; 56 | int dummy; 57 | 58 | bit __in_valid; 59 | longint __in_data; 60 | bit __in_last; 61 | longint __macaddr; 62 | 63 | reg __in_valid_reg; 64 | reg [63:0] __in_data_reg; 65 | reg __in_last_reg; 66 | reg [47:0] __macaddr_reg; 67 | 68 | initial begin 69 | dummy = $value$plusargs("netbw=%d", rlimit_gbps); 70 | dummy = $value$plusargs("netburst=%d", rlimit_size); 71 | dummy = $value$plusargs("netdev=%s", devname); 72 | network_init(devname, rlimit_gbps, rlimit_inc, rlimit_period); 73 | end 74 | 75 | /* verilator lint_off WIDTH */ 76 | always @(posedge clock) begin 77 | if (reset) begin 78 | __in_valid = 0; 79 | __in_data = 0; 80 | __in_last = 0; 81 | 82 | __in_valid_reg <= 1'b0; 83 | __in_data_reg <= 64'b0; 84 | __in_last_reg <= 1'b0; 85 | end else begin 86 | network_tick( 87 | net_out_valid, 88 | net_out_bits_data, 89 | net_out_bits_last, 90 | 91 | __in_valid, 92 | __in_data, 93 | __in_last, 94 | 95 | __macaddr); 96 | 97 | __in_valid_reg <= __in_valid; 98 | __in_data_reg <= __in_data; 99 | __in_last_reg <= __in_last; 100 | __macaddr_reg <= __macaddr; 101 | end 102 | end 103 | 104 | assign net_in_valid = __in_valid_reg; 105 | assign net_in_bits_data = __in_data_reg; 106 | assign net_in_bits_keep = 8'hff; 107 | assign net_in_bits_last = __in_last_reg; 108 | assign net_macAddr = __macaddr_reg; 109 | assign net_rlimit_inc = rlimit_inc; 110 | assign net_rlimit_period = rlimit_period; 111 | assign net_rlimit_size = rlimit_size; 112 | 113 | endmodule 114 | -------------------------------------------------------------------------------- /src/main/resources/vsrc/TraceROM.v: -------------------------------------------------------------------------------- 1 | import "DPI-C" function int trace_rom_init( 2 | input string filename 3 | ); 4 | 5 | import "DPI-C" function void trace_rom_tick( 6 | output bit stream_valid, 7 | input bit stream_ready, 8 | output longint stream_data, 9 | output byte stream_keep, 10 | output bit stream_last 11 | ); 12 | 13 | module TraceROM ( 14 | input clock, 15 | input reset, 16 | output stream_valid, 17 | input stream_ready, 18 | output [63:0] stream_bits_data, 19 | output [7:0] stream_bits_keep, 20 | output stream_bits_last, 21 | output [47:0] macAddr, 22 | output [31:0] length 23 | ); 24 | 25 | bit __stream_valid; 26 | longint __stream_data; 27 | byte __stream_keep; 28 | bit __stream_last; 29 | 30 | reg __stream_valid_reg; 31 | reg [63:0] __stream_data_reg; 32 | reg [7:0] __stream_keep_reg; 33 | reg __stream_last_reg; 34 | 35 | string fname; 36 | longint __macAddr; 37 | int __length; 38 | 39 | reg [47:0] macAddr_reg; 40 | reg [31:0] length_reg; 41 | 42 | assign macAddr = macAddr_reg; 43 | assign length = length_reg; 44 | 45 | assign stream_valid = __stream_valid_reg; 46 | assign stream_bits_data = __stream_data_reg; 47 | assign stream_bits_keep = __stream_keep_reg; 48 | assign stream_bits_last = __stream_last_reg; 49 | 50 | initial begin 51 | if ($value$plusargs("trace=%s", fname)) begin 52 | __length = trace_rom_init(fname); 53 | length_reg = __length; 54 | end 55 | if ($value$plusargs("macaddr=%x", __macAddr)) begin 56 | macAddr_reg = __macAddr; 57 | end 58 | end 59 | 60 | always @(posedge clock) begin 61 | if (reset) begin 62 | __stream_valid = 0; 63 | __stream_data = 0; 64 | __stream_keep = 0; 65 | __stream_last = 0; 66 | 67 | __stream_valid_reg <= 0; 68 | __stream_data_reg <= 0; 69 | __stream_keep_reg <= 0; 70 | __stream_last_reg <= 0; 71 | end else begin 72 | trace_rom_tick( 73 | __stream_valid, 74 | stream_ready, 75 | __stream_data, 76 | __stream_keep, 77 | __stream_last); 78 | 79 | __stream_valid_reg <= __stream_valid; 80 | __stream_data_reg <= __stream_data; 81 | __stream_keep_reg <= __stream_keep; 82 | __stream_last_reg <= __stream_last; 83 | end 84 | end 85 | endmodule 86 | -------------------------------------------------------------------------------- /src/main/scala/Aligner.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.unittest.UnitTest 6 | import IceNetConsts._ 7 | 8 | class Aligner(dataBits: Int) extends Module { 9 | val dataBytes = dataBits / 8 10 | 11 | val io = IO(new StreamIO(dataBits)) 12 | 13 | val data = RegInit(0.U(dataBits.W)) 14 | val keep = RegInit(0.U(dataBytes.W)) 15 | val last = RegInit(false.B) 16 | val nbytes = RegInit(0.U(log2Ceil(dataBytes + 1).W)) 17 | 18 | assert(!io.in.valid || io.in.bits.keep.orR, 19 | "Aligner cannot handle an empty flit") 20 | 21 | val rshift = PriorityEncoder(io.in.bits.keep) 22 | val full_keep = ((io.in.bits.keep >> rshift) << nbytes) | keep 23 | 24 | val in_mask = FillInterleaved(8, io.in.bits.keep) 25 | val in_data = io.in.bits.data & in_mask 26 | 27 | val rshift_bit = Cat(rshift, 0.U(3.W)) 28 | val nbits = Cat(nbytes, 0.U(3.W)) 29 | val bitmask = FillInterleaved(8, keep) 30 | val full_data = ((in_data >> rshift_bit) << nbits) | (data & bitmask) 31 | val full_nbytes = PopCount(full_keep) 32 | val fwd_last = io.in.bits.last && (full_keep >> dataBytes.U) === 0.U 33 | 34 | io.out.valid := (last && nbytes > 0.U) || 35 | (io.in.valid && (fwd_last || full_nbytes >= dataBytes.U)) 36 | io.out.bits.data := Mux(last, data, full_data(dataBits-1, 0)) 37 | io.out.bits.keep := Mux(last, keep, full_keep(dataBytes-1, 0)) 38 | io.out.bits.last := last || fwd_last 39 | 40 | io.in.ready := full_nbytes < dataBytes.U || 41 | (io.out.ready && !last) 42 | 43 | when (io.in.fire && io.out.fire) { 44 | data := full_data >> dataBits.U 45 | keep := full_keep >> dataBytes.U 46 | last := io.in.bits.last && !fwd_last 47 | nbytes := Mux(fwd_last, 0.U, full_nbytes - dataBytes.U) 48 | } .elsewhen (io.in.fire) { 49 | data := full_data 50 | keep := full_keep 51 | last := io.in.bits.last 52 | nbytes := full_nbytes 53 | } .elsewhen (io.out.fire) { 54 | data := 0.U 55 | keep := 0.U 56 | last := false.B 57 | nbytes := 0.U 58 | } 59 | } 60 | 61 | class AlignerTest extends UnitTest { 62 | val inData = VecInit( 63 | "h0011223344556677".U, 64 | "h8899AABBCCDDEEFF".U, 65 | "h0123456789ABCDEF".U, 66 | "hFEDCBA9876543210".U) 67 | val inKeep = VecInit( 68 | "b11111100".U, 69 | "b01111000".U, 70 | "b00001111".U, 71 | "b11110000".U) 72 | val inLast = VecInit(false.B, false.B, true.B, true.B) 73 | 74 | val outData = VecInit( 75 | "hBBCC001122334455".U, 76 | "h000089ABCDEF99AA".U, 77 | "h00000000FEDCBA98".U) 78 | val outKeep = VecInit( 79 | "b11111111".U, 80 | "b00111111".U, 81 | "b00001111".U) 82 | val outLast = VecInit(false.B, true.B, true.B) 83 | 84 | val started = RegInit(false.B) 85 | val sending = RegInit(false.B) 86 | val receiving = RegInit(false.B) 87 | 88 | val aligner = Module(new Aligner(NET_IF_WIDTH)) 89 | 90 | val (inIdx, inDone) = Counter(aligner.io.in.fire, inData.size) 91 | val (outIdx, outDone) = Counter(aligner.io.out.fire, outData.size) 92 | 93 | aligner.io.in.valid := sending 94 | aligner.io.in.bits.data := inData(inIdx) 95 | aligner.io.in.bits.keep := inKeep(inIdx) 96 | aligner.io.in.bits.last := inLast(inIdx) 97 | aligner.io.out.ready := receiving 98 | 99 | when (io.start && !started) { 100 | started := true.B 101 | sending := true.B 102 | receiving := true.B 103 | } 104 | when (inDone) { sending := false.B } 105 | when (outDone) { receiving := false.B } 106 | 107 | io.finished := started && !sending && !receiving 108 | 109 | def compareData(a: UInt, b: UInt, keep: UInt) = { 110 | val bitmask = FillInterleaved(8, keep) 111 | (a & bitmask) === (b & bitmask) 112 | } 113 | 114 | assert(!aligner.io.out.valid || 115 | (compareData( 116 | aligner.io.out.bits.data, 117 | outData(outIdx), 118 | aligner.io.out.bits.keep) && 119 | aligner.io.out.bits.keep === outKeep(outIdx) && 120 | aligner.io.out.bits.last === outLast(outIdx)), 121 | "AlignerTest: output does not match expected") 122 | } 123 | -------------------------------------------------------------------------------- /src/main/scala/Buffer.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.unittest.UnitTest 6 | import freechips.rocketchip.util.TwoWayCounter 7 | import scala.util.Random 8 | import IceNetConsts._ 9 | 10 | class BufferBRAM[T <: Data](n: Int, typ: T) extends Module { 11 | val addrBits = log2Ceil(n) 12 | val io = IO(new Bundle { 13 | // The value in data becomes valid one cycle after enable is asserted. 14 | // The value is held so long as enable is false 15 | val read = new Bundle { 16 | val en = Input(Bool()) 17 | val addr = Input(UInt(addrBits.W)) 18 | val data = Output(typ) 19 | } 20 | val write = new Bundle { 21 | val en = Input(Bool()) 22 | val addr = Input(UInt(addrBits.W)) 23 | val data = Input(typ) 24 | } 25 | }) 26 | 27 | val ram = Mem(n, typ) 28 | 29 | val wen = RegNext(io.write.en, false.B) 30 | val waddr = RegNext(io.write.addr) 31 | val wdata = RegNext(io.write.data) 32 | 33 | when (wen) { ram.write(waddr, wdata) } 34 | 35 | val rread_data = RegEnable(ram.read(io.read.addr), io.read.en) 36 | val rbypass = RegEnable(io.read.addr === waddr && wen, io.read.en) 37 | val rbypass_data = RegEnable(wdata, io.read.en) 38 | 39 | io.read.data := Mux(rbypass, rbypass_data, rread_data) 40 | } 41 | 42 | /** 43 | * Buffers incoming packets without backpressuring 44 | * Drops at packet boundaries if buffer fills us. 45 | * @bufWords Number of flits held by the buffer 46 | * @maxBytes Maximum number of bytes in a packet 47 | * @headerBytes Number of bytes in a header 48 | * @headerType Bundle type for header 49 | * @wordBytes Number of bytes in a flit 50 | * @dropChecks Sequence of functions that can trigger a drop by asserting Bool 51 | * @dropless If true, dropped packet causes assertion failure 52 | */ 53 | class NetworkPacketBuffer[T <: Data]( 54 | bufWords: Int, 55 | maxBytes: Int = ETH_JUMBO_MAX_BYTES, 56 | headerBytes: Int = ETH_HEAD_BYTES, 57 | headerType: T = new EthernetHeader, 58 | wordBytes: Int = NET_IF_WIDTH / 8, 59 | dropChecks: Seq[(T, StreamChannel, Bool) => Bool] = Nil, 60 | dropless: Boolean = false) extends Module { 61 | 62 | val maxWords = (maxBytes - 1) / wordBytes + 1 63 | val headerWords = headerBytes / wordBytes 64 | val wordBits = wordBytes * 8 65 | val nPackets = (bufWords - 1) / (headerWords + 1) + 1 66 | 67 | require(headerBytes % wordBytes == 0) 68 | 69 | val idxBits = log2Ceil(bufWords) 70 | val phaseBits = log2Ceil(nPackets + 1) 71 | val lenBits = log2Ceil(maxBytes + 1) 72 | val byteOffset = log2Ceil(wordBytes) 73 | 74 | val io = IO(new Bundle { 75 | val stream = new StreamIO(wordBytes * 8) 76 | val length = Valid(UInt(lenBits.W)) 77 | val count = Output(UInt(log2Ceil(nPackets+1).W)) 78 | val free = Output(UInt(8.W)) 79 | }) 80 | 81 | def discontinuous(bits: UInt, w: Int): Bool = 82 | (1 until w).map(i => bits(i) && !bits(i-1)).reduce(_ || _) 83 | 84 | assert(!io.stream.in.valid || 85 | Mux(io.stream.in.bits.last, 86 | !discontinuous(io.stream.in.bits.keep, wordBytes), 87 | io.stream.in.bits.keep.andR), 88 | "NetworkPacketBuffer does not handle missing data") 89 | 90 | val dataBuffer = Module(new BufferBRAM(bufWords, Bits(wordBits.W))) 91 | val headerVec = Reg(Vec(headerWords, Bits(wordBits.W))) 92 | val header = headerVec.asTypeOf(headerType) 93 | 94 | val lenBuffer = Module(new BufferBRAM(nPackets, UInt(lenBits.W))) 95 | 96 | val bufHead = RegInit(0.U(idxBits.W)) 97 | val bufTail = RegInit(0.U(idxBits.W)) 98 | val startHead = Reg(UInt(idxBits.W)) 99 | val revertHead = WireInit(false.B) 100 | 101 | val maybeFull = RegInit(false.B) 102 | val ptrMatch = bufHead === bufTail 103 | val bufFull = maybeFull && ptrMatch 104 | val bufEmpty = !maybeFull && ptrMatch 105 | 106 | val inIdx = RegInit(0.U(idxBits.W)) 107 | val inDrop = RegInit(false.B) 108 | val inLen = RegInit(0.U(lenBits.W)) 109 | val outIdx = RegInit(0.U(idxBits.W)) 110 | 111 | val inPhase = RegInit(0.U(phaseBits.W)) 112 | val outPhase = RegInit(0.U(phaseBits.W)) 113 | val pktCount = RegInit(0.U(phaseBits.W)) 114 | val nextLen = inLen + PopCount(io.stream.in.bits.keep) 115 | val hasPackets = pktCount > 0.U 116 | 117 | assert(pktCount <= nPackets.U, "Accepted more packets than possible") 118 | 119 | def bytesToWords(nbytes: UInt): UInt = 120 | nbytes(lenBits-1, byteOffset) + nbytes(byteOffset-1, 0).orR 121 | 122 | def finalKeep(nbytes: UInt): UInt = { 123 | val remBytes = nbytes(byteOffset-1, 0) 124 | val finalBytes = Mux(remBytes.orR, remBytes, wordBytes.U) 125 | (1.U << finalBytes) - 1.U 126 | } 127 | 128 | val length = lenBuffer.io.read.data 129 | val numWords = bytesToWords(length) 130 | // Need one cycle to read out packet length 131 | val lengthKnown = RegInit(false.B) 132 | val readLen = !lengthKnown && hasPackets 133 | 134 | val outLast = outIdx === (numWords - 1.U) 135 | val outValidReg = RegInit(false.B) 136 | 137 | val ren = (io.stream.out.ready || !outValidReg) && lengthKnown && !bufEmpty 138 | val wen = WireInit(false.B) 139 | val hwen = wen && inIdx < headerWords.U 140 | 141 | val setLength = WireInit(false.B) 142 | val clearLength = ren && outLast 143 | 144 | pktCount := pktCount + setLength - clearLength 145 | 146 | val outLastReg = RegEnable(outLast, ren) 147 | val outIdxReg = RegEnable(outIdx, ren) 148 | 149 | io.stream.out.valid := outValidReg 150 | io.stream.out.bits.data := dataBuffer.io.read.data 151 | io.stream.out.bits.last := outLastReg 152 | io.stream.out.bits.keep := Mux(outLastReg, finalKeep(length), ~0.U(wordBytes.W)) 153 | io.stream.in.ready := true.B 154 | io.length.bits := lenBuffer.io.read.data 155 | io.length.valid := lengthKnown 156 | io.count := pktCount 157 | 158 | def wrapInc(x: UInt, n: Int) = Mux(x === (n - 1).U, 0.U, x + 1.U) 159 | 160 | dataBuffer.io.read.en := ren 161 | dataBuffer.io.read.addr := bufTail 162 | dataBuffer.io.write.en := wen 163 | dataBuffer.io.write.addr := bufHead 164 | dataBuffer.io.write.data := io.stream.in.bits.data 165 | 166 | lenBuffer.io.read.en := readLen 167 | lenBuffer.io.read.addr := outPhase 168 | lenBuffer.io.write.en := setLength 169 | lenBuffer.io.write.addr := inPhase 170 | lenBuffer.io.write.data := nextLen 171 | 172 | val headerValid = inIdx >= headerWords.U 173 | val customDrop = dropChecks.map(check => check( 174 | header, 175 | io.stream.in.bits, 176 | io.stream.in.fire && headerValid)) 177 | .foldLeft(false.B)(_ || _) 178 | val startDropping = 179 | (inPhase === outPhase && hasPackets) || 180 | (inIdx === maxWords.U) || bufFull 181 | 182 | val capDrop = startDropping || inDrop 183 | val nonCapDrop = !headerValid || customDrop 184 | val anyDrop = capDrop || nonCapDrop 185 | val dropLastFire = anyDrop && io.stream.in.fire && io.stream.in.bits.last 186 | 187 | // io.free indicates the number of flits being freed up on a cycle 188 | io.free := ren + Mux(dropLastFire, inIdx + 1.U, 0.U) 189 | 190 | when (io.stream.out.fire) { outValidReg := false.B } 191 | 192 | when (readLen) { lengthKnown := true.B } 193 | 194 | when (ren) { 195 | outValidReg := true.B 196 | bufTail := wrapInc(bufTail, bufWords) 197 | outIdx := outIdx + 1.U 198 | 199 | when (outLast) { 200 | outIdx := 0.U 201 | outPhase := wrapInc(outPhase, nPackets) 202 | lengthKnown := false.B 203 | } 204 | } 205 | 206 | when (hwen) { headerVec(inIdx) := io.stream.in.bits.data } 207 | 208 | when (wen) { bufHead := wrapInc(bufHead, bufWords) } 209 | 210 | when (ren =/= wen) { maybeFull := wen } 211 | 212 | when (io.stream.in.fire) { 213 | when (inIdx === 0.U) { startHead := bufHead } 214 | when (startDropping) { inDrop := true.B } 215 | wen := !startDropping && !inDrop 216 | when (inIdx =/= (bufWords - 1).U) { inIdx := inIdx + 1.U } 217 | 218 | inLen := nextLen 219 | 220 | when (io.stream.in.bits.last) { 221 | val nextPhase = wrapInc(inPhase, nPackets) 222 | // Drop packets if there aren't more than headerBytes amount of data 223 | when (!anyDrop) { 224 | setLength := true.B 225 | inPhase := nextPhase 226 | } .otherwise { 227 | wen := false.B 228 | revertHead := inIdx =/= 0.U 229 | if (dropless) { 230 | assert(!capDrop, 231 | "Packet dropped by buffer due to insufficient capacity") 232 | } else { 233 | printf("WARNING: dropped packet with %d flits\n", inIdx + 1.U) 234 | } 235 | } 236 | inIdx := 0.U 237 | inLen := 0.U 238 | inDrop := false.B 239 | } 240 | } 241 | 242 | when (revertHead) { 243 | bufHead := startHead 244 | maybeFull := false.B 245 | } 246 | } 247 | 248 | class NetworkPacketBufferTest extends UnitTest(100000) { 249 | val buffer = Module(new NetworkPacketBuffer( 250 | bufWords = 12, 251 | maxBytes = 32, 252 | headerBytes = 8, 253 | headerType = UInt(64.W), 254 | wordBytes = 4)) 255 | 256 | val inPackets = Seq( 257 | (10, false), // drop because too long 258 | (4, true), 259 | (1, false), // drop because too short 260 | (5, false), 261 | (7, true), 262 | (8, false), 263 | (6, false), // drop because buffer full 264 | (4, true), 265 | (3, false), 266 | (5, true), 267 | (6, false), 268 | (4, true), 269 | (5, true)) 270 | 271 | val outPackets = Seq( 272 | (4, true), 273 | (5, false), 274 | (7, true), 275 | (8, false), 276 | (4, true), 277 | (3, false), 278 | (5, true), 279 | (6, true), 280 | (4, false), 281 | (5, true)) 282 | 283 | val phaseBits = log2Ceil(inPackets.length) 284 | val idxBits = 4 285 | 286 | val inPacketLengths = VecInit(inPackets.map { 287 | case (x, _) => (x - 1).U(idxBits.W) 288 | }) 289 | val inPacketSwitch = VecInit(inPackets.map(_._2.B)) 290 | 291 | val outPacketLengths = VecInit(outPackets.map { 292 | case (x, _) => (x - 1).U(idxBits.W) 293 | }) 294 | val outPacketSwitch = VecInit(outPackets.map(_._2.B)) 295 | 296 | val inPhase = RegInit(0.U(phaseBits.W)) 297 | val outPhase = RegInit(0.U(phaseBits.W)) 298 | 299 | val inIdx = RegInit(0.U(idxBits.W)) 300 | val outIdx = RegInit(0.U(idxBits.W)) 301 | 302 | val s_start :: s_input :: s_output :: s_done :: Nil = Enum(4) 303 | val state = RegInit(s_start) 304 | 305 | buffer.io.stream.in.valid := state === s_input 306 | buffer.io.stream.in.bits.data := inIdx 307 | buffer.io.stream.in.bits.keep := ~0.U(32.W) 308 | buffer.io.stream.in.bits.last := inIdx === inPacketLengths(inPhase) 309 | buffer.io.stream.out.ready := state === s_output 310 | 311 | when (io.start && state === s_start) { 312 | state := s_input 313 | } 314 | 315 | when (buffer.io.stream.in.fire) { 316 | inIdx := inIdx + 1.U 317 | when (buffer.io.stream.in.bits.last) { 318 | inIdx := 0.U 319 | inPhase := inPhase + 1.U 320 | when (inPacketSwitch(inPhase)) { state := s_output } 321 | } 322 | } 323 | 324 | when (buffer.io.stream.out.fire) { 325 | outIdx := outIdx + 1.U 326 | when (buffer.io.stream.out.bits.last) { 327 | outIdx := 0.U 328 | outPhase := outPhase + 1.U 329 | when (outPacketSwitch(outPhase)) { state := s_input } 330 | when (outPhase === (outPackets.length - 1).U) { state := s_done } 331 | } 332 | } 333 | 334 | assert(!buffer.io.stream.out.valid || buffer.io.stream.out.bits.data === outIdx, 335 | "NetworkPacketBufferTest: got wrong output data") 336 | assert(!buffer.io.stream.out.valid || !buffer.io.stream.out.bits.last || 337 | outIdx === outPacketLengths(outPhase), 338 | "NetworkPacketBufferTest: got output packet with wrong length") 339 | 340 | io.finished := state === s_done 341 | } 342 | 343 | class ReservationBufferAlloc(nXacts: Int, nWords: Int) extends Bundle { 344 | private val xactIdBits = log2Ceil(nXacts) 345 | private val countBits = log2Ceil(nWords + 1) 346 | 347 | val id = UInt(xactIdBits.W) 348 | val count = UInt(countBits.W) 349 | 350 | } 351 | 352 | class ReservationBufferData(nXacts: Int, dataBits: Int) extends Bundle { 353 | private val xactIdBits = log2Ceil(nXacts) 354 | 355 | val id = UInt(xactIdBits.W) 356 | val data = new StreamChannel(dataBits) 357 | 358 | } 359 | 360 | class ReservationBuffer(nXacts: Int, nWords: Int, dataBits: Int) extends Module { 361 | private val xactIdBits = log2Ceil(nXacts) 362 | private val countBits = log2Ceil(nWords + 1) 363 | 364 | require(nXacts <= nWords) 365 | 366 | val io = IO(new Bundle { 367 | val alloc = Flipped(Decoupled(new ReservationBufferAlloc(nXacts, nWords))) 368 | val in = Flipped(Decoupled(new ReservationBufferData(nXacts, dataBits))) 369 | val out = Decoupled(new StreamChannel(dataBits)) 370 | }) 371 | 372 | def incWrap(cur: UInt, inc: UInt): UInt = { 373 | val unwrapped = cur +& inc 374 | Mux(unwrapped >= nWords.U, unwrapped - nWords.U, unwrapped) 375 | } 376 | 377 | val buffer = Module(new BufferBRAM(nWords, new StreamChannel(dataBits))) 378 | val bufValid = RegInit(0.U(nWords.W)) 379 | 380 | val head = RegInit(0.U(countBits.W)) 381 | val tail = RegInit(0.U(countBits.W)) 382 | val count = RegInit(0.U(countBits.W)) 383 | 384 | val full = (count + io.alloc.bits.count) > nWords.U 385 | val xactHeads = Reg(Vec(nXacts, UInt(countBits.W))) 386 | val curXactHead = xactHeads(io.in.bits.id) 387 | 388 | val occupied = RegInit(false.B) 389 | val ren = (!occupied || io.out.ready) && (bufValid >> tail)(0) 390 | 391 | count := count + 392 | Mux(io.alloc.fire, io.alloc.bits.count, 0.U) - 393 | Mux(io.out.fire, 1.U, 0.U) 394 | bufValid := (bufValid | Mux(io.in.fire, UIntToOH(curXactHead), 0.U)) & 395 | ~Mux(ren, UIntToOH(tail), 0.U) 396 | 397 | io.alloc.ready := !full 398 | io.in.ready := true.B 399 | io.out.valid := occupied 400 | io.out.bits := buffer.io.read.data 401 | 402 | buffer.io.write.en := io.in.fire 403 | buffer.io.write.addr := curXactHead 404 | buffer.io.write.data := io.in.bits.data 405 | buffer.io.read.en := ren 406 | buffer.io.read.addr := tail 407 | 408 | when (io.alloc.fire) { 409 | xactHeads(io.alloc.bits.id) := head 410 | head := incWrap(head, io.alloc.bits.count) 411 | } 412 | 413 | when (io.in.fire) { 414 | xactHeads(io.in.bits.id) := incWrap(curXactHead, 1.U) 415 | } 416 | 417 | when (io.out.fire) { occupied := false.B } 418 | 419 | when (ren) { 420 | occupied := true.B 421 | tail := incWrap(tail, 1.U) 422 | } 423 | } 424 | 425 | class PacketCollectionBuffer(bufWords: Int) extends Module { 426 | val io = IO(new StreamIO(NET_IF_WIDTH)) 427 | 428 | val headerWords = ETH_HEAD_BYTES / NET_IF_BYTES 429 | val maxPackets = (bufWords - 1) / (headerWords + 1) + 1 430 | 431 | val queue = Module(new Queue(new StreamChannel(NET_IF_WIDTH), bufWords)) 432 | val pktCount = TwoWayCounter( 433 | queue.io.enq.fire && queue.io.enq.bits.last, 434 | queue.io.deq.fire && queue.io.deq.bits.last, 435 | maxPackets) 436 | val canDequeue = pktCount > 0.U 437 | 438 | queue.io.enq <> io.in 439 | io.out.valid := queue.io.deq.valid && canDequeue 440 | io.out.bits := queue.io.deq.bits 441 | queue.io.deq.ready := io.out.ready && canDequeue 442 | } 443 | -------------------------------------------------------------------------------- /src/main/scala/Checksum.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.unittest.UnitTest 6 | import freechips.rocketchip.util.{DecoupledHelper, UIntIsOneOf} 7 | import IceNetConsts._ 8 | import NetworkHelpers._ 9 | 10 | class ChecksumCalcRequest extends Bundle { 11 | val check = Bool() 12 | val start = UInt(16.W) 13 | val init = UInt(16.W) 14 | } 15 | 16 | class ChecksumRewriteRequest extends Bundle { 17 | val check = Bool() 18 | val offset = UInt(16.W) 19 | val start = UInt(16.W) 20 | val init = UInt(16.W) 21 | } 22 | 23 | class ChecksumCalc(dataBits: Int) extends Module { 24 | val dataBytes = dataBits / 8 25 | 26 | val io = IO(new Bundle { 27 | val req = Flipped(Decoupled(new ChecksumCalcRequest)) 28 | val stream = new StreamIO(dataBits) 29 | val result = Decoupled(UInt(16.W)) 30 | }) 31 | 32 | val csum = Reg(UInt((dataBits + 16).W)) 33 | val check = Reg(Bool()) 34 | val start = Reg(UInt(16.W)) 35 | val startPos = Reg(UInt(16.W)) 36 | val nextStartPos = startPos + dataBytes.U 37 | val sumMask = (0 until dataBytes).map { i => 38 | io.stream.in.bits.keep(i) && (startPos + i.U) >= start 39 | } 40 | val sumData = io.stream.in.bits.data & FillInterleaved(8, sumMask) 41 | 42 | val s_req :: s_stream :: s_fold :: s_result :: Nil = Enum(4) 43 | val state = RegInit(s_req) 44 | 45 | io.req.ready := state === s_req 46 | io.stream.out.valid := state === s_stream && io.stream.in.valid 47 | io.stream.in.ready := state === s_stream && io.stream.out.ready 48 | io.stream.out.bits := io.stream.in.bits 49 | io.result.valid := state === s_result 50 | io.result.bits := csum(15, 0) 51 | 52 | when (io.req.fire) { 53 | check := io.req.bits.check 54 | start := io.req.bits.start 55 | csum := io.req.bits.init 56 | startPos := 0.U 57 | state := s_stream 58 | } 59 | 60 | when (io.stream.in.fire) { 61 | when (check) { 62 | csum := csum + sumData 63 | startPos := nextStartPos 64 | } 65 | 66 | when (io.stream.in.bits.last) { 67 | state := Mux(check, s_fold, s_req) 68 | } 69 | } 70 | 71 | when (state === s_fold) { 72 | val upper = csum(15 + dataBits, 16) 73 | val lower = csum(15, 0) 74 | 75 | when (upper === 0.U) { 76 | csum := ~lower 77 | state := s_result 78 | } .otherwise { 79 | csum := upper + lower 80 | } 81 | } 82 | 83 | when (io.result.fire) { state := s_req } 84 | } 85 | 86 | class ChecksumRewrite(dataBits: Int, nBufFlits: Int) extends Module { 87 | val dataBytes = dataBits / 8 88 | 89 | val io = IO(new Bundle { 90 | val req = Flipped(Decoupled(new ChecksumRewriteRequest)) 91 | val stream = new StreamIO(dataBits) 92 | }) 93 | 94 | val reqq = Module(new Queue(new ChecksumRewriteRequest, 2)) 95 | val calc = Module(new ChecksumCalc(dataBits)) 96 | val buffer = Module(new Queue(new StreamChannel(dataBits), nBufFlits)) 97 | val offset = Reg(UInt(16.W)) 98 | val check = Reg(Bool()) 99 | val csum = Reg(UInt(16.W)) 100 | 101 | val reqHelper = DecoupledHelper( 102 | io.req.valid, 103 | calc.io.req.ready, 104 | reqq.io.enq.ready) 105 | 106 | io.req.ready := reqHelper.fire(io.req.valid) 107 | calc.io.req.valid := reqHelper.fire(calc.io.req.ready) 108 | calc.io.req.bits.check := io.req.bits.check 109 | calc.io.req.bits.start := io.req.bits.start 110 | calc.io.req.bits.init := io.req.bits.init 111 | reqq.io.enq.valid := reqHelper.fire(reqq.io.enq.ready) 112 | reqq.io.enq.bits := io.req.bits 113 | calc.io.stream.in <> io.stream.in 114 | buffer.io.enq <> calc.io.stream.out 115 | 116 | val byteOffBits = log2Ceil(dataBytes) 117 | val startPos = Reg(UInt(16.W)) 118 | val nextStartPos = startPos + dataBytes.U 119 | val baseData = buffer.io.deq.bits.data 120 | val shiftAmt = Cat((offset - startPos)(byteOffBits-1, 0), 0.U(3.W)) 121 | val dataMask = ~(~0.U(16.W) << shiftAmt) 122 | val csumShifted = csum << shiftAmt 123 | val replace = check && (offset >= startPos) && (offset < nextStartPos) 124 | val outData = Mux(replace, (baseData & dataMask) | csumShifted, baseData) 125 | 126 | val s_req :: s_wait :: s_flush :: Nil = Enum(3) 127 | val state = RegInit(s_req) 128 | 129 | when (reqq.io.deq.fire) { 130 | check := reqq.io.deq.bits.check 131 | offset := reqq.io.deq.bits.offset 132 | startPos := 0.U 133 | state := Mux(reqq.io.deq.bits.check, s_wait, s_flush) 134 | } 135 | 136 | when (calc.io.result.fire) { 137 | csum := calc.io.result.bits 138 | state := s_flush 139 | } 140 | 141 | when (io.stream.out.fire) { 142 | startPos := nextStartPos 143 | when (io.stream.out.bits.last) { state := s_req } 144 | } 145 | 146 | val deqOK = (state === s_flush || nextStartPos <= offset) 147 | 148 | reqq.io.deq.ready := state === s_req 149 | calc.io.result.ready := state === s_wait 150 | 151 | io.stream.out.valid := buffer.io.deq.valid && deqOK 152 | buffer.io.deq.ready := io.stream.out.ready && deqOK 153 | io.stream.out.bits := buffer.io.deq.bits 154 | io.stream.out.bits.data := outData 155 | } 156 | 157 | class ChecksumTest extends UnitTest { 158 | val offset = 6 159 | val init = 0x4315 160 | val start = 2 161 | val data = Seq(0xdead, 0xbeef, 0x7432, 0x0000, 0xf00d, 0x3163, 0x9821, 0x1543) 162 | val take = Seq(true, true, true, true, true, true, true, false) 163 | val keep = take.map(if (_) 0x3 else 0x0) 164 | 165 | var csum = init + data.zip(take).drop(start/2) 166 | .filter(_._2).map(_._1).reduce(_ + _) 167 | while (csum > 0xffff) { 168 | csum = (csum >> 16) + (csum & 0xffff) 169 | } 170 | csum = ~csum & 0xffff 171 | 172 | val expected = data.take(offset/2) ++ 173 | Seq(csum) ++ data.drop(offset/2+1) 174 | 175 | def seqToVec(seq: Seq[Int], step: Int, nbits: Int) = { 176 | VecInit((0 until seq.length by step).map { i => 177 | Cat((i until (i + step)).map(seq(_).U(nbits.W)).reverse) 178 | }) 179 | } 180 | 181 | val dataBits = 32 182 | val dataBytes = dataBits / 8 183 | val shortsPerFlit = dataBits / 16 184 | val dataVec = seqToVec(data, shortsPerFlit, 16) 185 | val expectedVec = seqToVec(expected, shortsPerFlit, 16) 186 | val keepVec = seqToVec(keep, shortsPerFlit, 2) 187 | 188 | val s_start :: s_req :: s_input :: s_output :: s_done :: Nil = Enum(5) 189 | val state = RegInit(s_start) 190 | 191 | val rewriter = Module(new ChecksumRewrite( 192 | dataBits, data.length/shortsPerFlit)) 193 | 194 | val (inIdx, inDone) = Counter(rewriter.io.stream.in.fire, dataVec.length) 195 | val (outIdx, outDone) = Counter(rewriter.io.stream.out.fire, expectedVec.length) 196 | 197 | rewriter.io.req.valid := state === s_req 198 | rewriter.io.req.bits.check := true.B 199 | rewriter.io.req.bits.start := start.U 200 | rewriter.io.req.bits.init := init.U 201 | rewriter.io.req.bits.offset := offset.U 202 | rewriter.io.stream.in.valid := state === s_input 203 | rewriter.io.stream.in.bits.data := dataVec(inIdx) 204 | rewriter.io.stream.in.bits.keep := keepVec(inIdx) 205 | rewriter.io.stream.in.bits.last := inIdx === (dataVec.length-1).U 206 | rewriter.io.stream.out.ready := state === s_output 207 | io.finished := state === s_done 208 | 209 | when (state === s_start && io.start) { state := s_req } 210 | when (rewriter.io.req.fire) { state := s_input } 211 | when (inDone) { state := s_output } 212 | when (outDone) { state := s_done } 213 | 214 | assert(!rewriter.io.stream.out.valid || 215 | rewriter.io.stream.out.bits.data === expectedVec(outIdx), 216 | "ChecksumTest: got wrong data") 217 | 218 | assert(!rewriter.io.stream.out.valid || 219 | rewriter.io.stream.out.bits.keep === keepVec(outIdx), 220 | "ChecksumTest: got wrong keep") 221 | } 222 | 223 | class TCPChecksumOffloadResult extends Bundle { 224 | val correct = Bool() 225 | val checked = Bool() 226 | } 227 | 228 | class TCPChecksumOffload(dataBits: Int) extends Module { 229 | val io = IO(new Bundle { 230 | val in = Flipped(Decoupled(new StreamChannel(dataBits))) 231 | val result = Decoupled(new TCPChecksumOffloadResult) 232 | }) 233 | 234 | class FullHeader extends Bundle { 235 | val tcp = new TCPHeader 236 | val ipv4 = new IPv4Header 237 | val eth = new EthernetHeader 238 | } 239 | class PseudoHeader extends Bundle { 240 | val tcp = new TCPHeader 241 | val length = UInt(16.W) 242 | val protocol = UInt(8.W) 243 | val zeros = UInt(8.W) 244 | val dest_ip = UInt(32.W) 245 | val source_ip = UInt(32.W) 246 | } 247 | 248 | val dataBytes = dataBits/8 249 | 250 | val headerBytes = ETH_HEAD_BYTES + IPV4_HEAD_BYTES + TCP_HEAD_BYTES 251 | val headerWords = headerBytes / dataBytes 252 | val headerVec = Reg(Vec(headerWords, UInt(dataBits.W))) 253 | val header = headerVec.asTypeOf(new FullHeader) 254 | val headerIdx = RegInit(0.U(log2Ceil(headerWords).W)) 255 | 256 | val pseudoHeaderBytes = 12 + TCP_HEAD_BYTES 257 | val pseudoHeaderWords = pseudoHeaderBytes / dataBytes 258 | val pseudoHeader = Wire(new PseudoHeader) 259 | val pseudoHeaderVec = pseudoHeader.asTypeOf( 260 | Vec(pseudoHeaderWords, UInt(dataBits.W))) 261 | 262 | pseudoHeader.tcp := header.tcp 263 | pseudoHeader.length := htons(ntohs(header.ipv4.total_length) - IPV4_HEAD_BYTES.U) 264 | pseudoHeader.protocol := header.ipv4.protocol 265 | pseudoHeader.zeros := 0.U 266 | pseudoHeader.dest_ip := header.ipv4.dest_ip 267 | pseudoHeader.source_ip := header.ipv4.source_ip 268 | 269 | require(dataBits >= 16) 270 | require(headerBytes % dataBytes == 0) 271 | require(pseudoHeaderBytes % dataBytes == 0) 272 | 273 | val (s_header_in :: s_csum_req :: 274 | s_header_csum :: s_body_csum :: 275 | s_passthru :: s_result :: Nil) = Enum(6) 276 | val state = RegInit(s_header_in) 277 | 278 | val headerOK = 279 | header.eth.ethType === IPV4_ETHTYPE.U && 280 | header.ipv4.protocol === TCP_PROTOCOL.U && 281 | header.ipv4.ihl === 5.U 282 | val headerChannel = Wire(new StreamChannel(dataBits)) 283 | headerChannel.data := pseudoHeaderVec(headerIdx) 284 | headerChannel.keep := ~0.U(dataBits.W) 285 | headerChannel.last := false.B 286 | 287 | val resultExpected = RegInit(false.B) 288 | 289 | val csum = Module(new ChecksumCalc(dataBits)) 290 | 291 | csum.io.req.valid := state === s_csum_req 292 | csum.io.req.bits.check := true.B 293 | csum.io.req.bits.start := 0.U 294 | csum.io.req.bits.init := 0.U 295 | 296 | csum.io.stream.in.valid := 297 | (state === s_header_csum) || (state === s_body_csum && io.in.valid) 298 | csum.io.stream.in.bits := Mux( 299 | state === s_header_csum, headerChannel, io.in.bits) 300 | csum.io.stream.out.ready := true.B // just ignore output 301 | 302 | io.in.ready := state.isOneOf(s_header_in, s_passthru) || 303 | (state === s_body_csum && csum.io.stream.in.ready) 304 | 305 | io.result.valid := state === s_result && (!resultExpected || csum.io.result.valid) 306 | io.result.bits.correct := csum.io.result.bits === 0.U 307 | io.result.bits.checked := resultExpected 308 | csum.io.result.ready := state === s_result && resultExpected && io.result.ready 309 | 310 | when (io.in.fire) { 311 | when (io.in.bits.last) { 312 | state := s_result 313 | } .elsewhen (state === s_header_in) { 314 | headerVec(headerIdx) := io.in.bits.data 315 | headerIdx := headerIdx + 1.U 316 | when (headerIdx === (headerWords-1).U) { 317 | resultExpected := headerOK 318 | state := Mux(headerOK, s_csum_req, s_passthru) 319 | } 320 | } 321 | } 322 | 323 | when (csum.io.req.fire) { 324 | headerIdx := 0.U 325 | state := s_header_csum 326 | } 327 | 328 | when (state === s_header_csum && csum.io.stream.in.ready) { 329 | headerIdx := headerIdx + 1.U 330 | when (headerIdx === (pseudoHeaderWords-1).U) { 331 | state := s_body_csum 332 | } 333 | } 334 | 335 | when (io.result.fire) { 336 | headerIdx := 0.U 337 | resultExpected := false.B 338 | state := s_header_in 339 | } 340 | } 341 | 342 | class ChecksumTCPVerify extends UnitTest { 343 | val packets = Seq("""00 00 00 12 6d 00 00 03 00 12 6d 00 00 02 08 00 344 | |45 00 00 3c 45 9b 40 00 40 06 9c fb ac 10 00 02 345 | |ac 10 00 03 c2 a8 14 51 22 ad 9e d6 00 00 00 00 346 | |a0 02 72 10 bf 07 00 00 02 04 05 b4 04 02 08 0a 347 | |43 ec e2 58 00 00 00 00 01 03 03 07 00 00 00 00""", 348 | """00 00 00 12 6d 00 00 02 00 12 6d 00 00 03 08 06 349 | |00 01 08 00 06 04 00 02 00 12 6d 00 00 03 ac 10 350 | |00 03 00 12 6d 00 00 02 ac 10 00 02 00 02 00 00""") 351 | val dataBytes = packets.map(packetStr => 352 | packetStr.stripMargin.replaceAll("\n", " ") 353 | .split(" ").map(BigInt(_, 16))) 354 | val dataWords = VecInit(dataBytes.flatMap( 355 | bytes => (0 until bytes.length by 8).map( 356 | i => Cat(bytes.slice(i, i + 8).map(_.U(8.W)).reverse)))) 357 | val dataLast = VecInit(dataBytes.map(_.length / 8).flatMap( 358 | nWords => Seq.fill(nWords-1)(false.B) :+ true.B)) 359 | 360 | val expectedResults = VecInit(3.U, 2.U) 361 | 362 | val started = RegInit(false.B) 363 | val inputValid = RegInit(false.B) 364 | val outputReady = RegInit(false.B) 365 | val resultReady = RegInit(false.B) 366 | 367 | val offload = Module(new TCPChecksumOffload(NET_IF_WIDTH)) 368 | 369 | val (inIdx, inDone) = Counter(offload.io.in.fire, dataWords.length) 370 | val (resultIdx, resultDone) = Counter(offload.io.result.fire, expectedResults.length) 371 | 372 | offload.io.in.valid := inputValid 373 | offload.io.in.bits.data := dataWords(inIdx) 374 | offload.io.in.bits.keep := NET_FULL_KEEP 375 | offload.io.in.bits.last := dataLast(inIdx) 376 | offload.io.result.ready := resultReady 377 | io.finished := started && !inputValid && !resultReady 378 | 379 | when (!started && io.start) { 380 | started := true.B 381 | inputValid := true.B 382 | resultReady := true.B 383 | } 384 | when (inDone) { inputValid := false.B } 385 | when (resultDone) { resultReady := false.B } 386 | 387 | assert(!offload.io.result.valid || 388 | offload.io.result.bits.asUInt === expectedResults(resultIdx), 389 | "ChecksumTCPVerify: checksum was not correct") 390 | } 391 | -------------------------------------------------------------------------------- /src/main/scala/Configs.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import freechips.rocketchip.subsystem.BaseSubsystemConfig 5 | import org.chipsalliance.cde.config.{Parameters, Config} 6 | import freechips.rocketchip.unittest.UnitTests 7 | 8 | class WithIceNetUnitTests extends Config((site, here, up) => { 9 | case NICKey => Some(NICConfig()) 10 | case UnitTests => (p: Parameters) => { 11 | Seq( 12 | Module(new NetworkPacketBufferTest), 13 | Module(new PauserTest), 14 | Module(new NetworkTapTest), 15 | Module(new RateLimiterTest), 16 | Module(new AlignerTest), 17 | Module(new ChecksumTest), 18 | Module(new ChecksumTCPVerify), 19 | Module(new IceNicSendTestWrapper()(p)), 20 | Module(new IceNicRecvTestWrapper()(p)), 21 | Module(new IceNicTestWrapper()(p)), 22 | Module(new MisalignedTestWrapper()(p))) 23 | } 24 | }) 25 | 26 | class IceNetUnitTestConfig extends Config( 27 | new WithIceNetUnitTests ++ new BaseSubsystemConfig) 28 | 29 | class WithIceNIC(inBufFlits: Int = 1800, usePauser: Boolean = false, ctrlQueueDepth: Int = 64) 30 | extends Config((site, here, up) => { 31 | case NICKey => Some(NICConfig( 32 | inBufFlits = inBufFlits, 33 | ctrlQueueDepth = ctrlQueueDepth, 34 | usePauser = usePauser, 35 | checksumOffload = true)) 36 | }) 37 | 38 | class WithNoIceNIC extends Config((site, here, up) => { 39 | case NICKey => None 40 | }) 41 | -------------------------------------------------------------------------------- /src/main/scala/Consts.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | 5 | object IceNetConsts { 6 | val NET_IF_WIDTH = 64 7 | val NET_IF_BYTES = NET_IF_WIDTH/8 8 | val NET_LEN_BITS = 16 9 | 10 | val NET_IP_ALIGN = 2 11 | val ETH_HEAD_BYTES = 16 12 | 13 | val ETH_STANDARD_MTU = 1500 14 | val ETH_STANDARD_MAX_BYTES = ETH_STANDARD_MTU + ETH_HEAD_BYTES 15 | val ETH_STANDARD_MAX_FLITS = (ETH_STANDARD_MAX_BYTES - 1) / NET_IF_BYTES + 1 16 | 17 | val ETH_JUMBO_MTU = 9000 18 | val ETH_JUMBO_MAX_BYTES = ETH_JUMBO_MTU + ETH_HEAD_BYTES 19 | val ETH_JUMBO_MAX_FLITS = (ETH_JUMBO_MAX_BYTES - 1) / NET_IF_BYTES + 1 20 | 21 | val ETH_SUPER_JUMBO_MTU = 64 << 10 22 | val ETH_SUPER_JUMBO_MAX_BYTES = ETH_SUPER_JUMBO_MTU + ETH_HEAD_BYTES 23 | val ETH_SUPER_JUMBO_MAX_FLITS = (ETH_SUPER_JUMBO_MAX_BYTES - 1) / NET_IF_BYTES + 1 24 | 25 | val ETH_MAC_BITS = 48 26 | val ETH_TYPE_BITS = 16 27 | val ETH_PAD_BITS = 16 28 | 29 | val IPV4_HEAD_BYTES = 20 30 | val UDP_HEAD_BYTES = 8 31 | val TCP_HEAD_BYTES = 20 32 | 33 | val RLIMIT_MAX_INC = 256 34 | val RLIMIT_MAX_PERIOD = 256 35 | val RLIMIT_MAX_SIZE = 256 36 | 37 | def NET_FULL_KEEP = ~0.U(NET_IF_BYTES.W) 38 | def ETH_BCAST_MAC = ~0.U(ETH_MAC_BITS.W) 39 | 40 | val IPV4_ETHTYPE = 0x0008 41 | val TCP_PROTOCOL = 0x06 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/DMA.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import org.chipsalliance.cde.config.Parameters 6 | import freechips.rocketchip.diplomacy.{LazyModule, LazyModuleImp, IdRange} 7 | import freechips.rocketchip.util.DecoupledHelper 8 | import freechips.rocketchip.tilelink._ 9 | 10 | class StreamReadRequest extends Bundle { 11 | val address = UInt(48.W) 12 | val length = UInt(15.W) 13 | val partial = Bool() 14 | } 15 | 16 | class StreamReader(nXacts: Int, outFlits: Int, maxBytes: Int) 17 | (implicit p: Parameters) extends LazyModule { 18 | 19 | val core = LazyModule(new StreamReaderCore(nXacts, outFlits, maxBytes)) 20 | val node = core.node 21 | 22 | lazy val module = new Impl 23 | class Impl extends LazyModuleImp(this) { 24 | val dataBits = core.module.dataBits 25 | 26 | val io = IO(new Bundle { 27 | val req = Flipped(Decoupled(new StreamReadRequest)) 28 | val resp = Decoupled(Bool()) 29 | val out = Decoupled(new StreamChannel(dataBits)) 30 | }) 31 | 32 | core.module.io.req <> io.req 33 | io.resp <> core.module.io.resp 34 | 35 | val buffer = Module(new ReservationBuffer(nXacts, outFlits, dataBits)) 36 | buffer.io.alloc <> core.module.io.alloc 37 | buffer.io.in <> core.module.io.out 38 | 39 | val aligner = Module(new Aligner(dataBits)) 40 | aligner.io.in <> buffer.io.out 41 | io.out <> aligner.io.out 42 | } 43 | } 44 | 45 | class StreamReaderCore(nXacts: Int, outFlits: Int, maxBytes: Int) 46 | (implicit p: Parameters) extends LazyModule { 47 | val node = TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLClientParameters( 48 | name = "stream-reader", sourceId = IdRange(0, nXacts)))))) 49 | 50 | lazy val module = new Impl 51 | class Impl extends LazyModuleImp(this) { 52 | val (tl, edge) = node.out(0) 53 | val dataBits = tl.params.dataBits 54 | val beatBytes = dataBits / 8 55 | val byteAddrBits = log2Ceil(beatBytes) 56 | val addrBits = tl.params.addressBits 57 | val lenBits = 15 58 | 59 | require (edge.manager.minLatency > 0) 60 | 61 | val io = IO(new Bundle { 62 | val req = Flipped(Decoupled(new StreamReadRequest)) 63 | val resp = Decoupled(Bool()) 64 | val alloc = Decoupled(new ReservationBufferAlloc(nXacts, outFlits)) 65 | val out = Decoupled(new ReservationBufferData(nXacts, dataBits)) 66 | }) 67 | 68 | val s_idle :: s_read :: s_resp :: Nil = Enum(3) 69 | val state = RegInit(s_idle) 70 | 71 | // Physical (word) address in memory 72 | val sendaddr = Reg(UInt(addrBits.W)) 73 | // Number of words to send 74 | val sendlen = Reg(UInt(lenBits.W)) 75 | // 0 if last packet in sequence, 1 otherwise 76 | val sendpart = Reg(Bool()) 77 | 78 | val xactBusy = RegInit(0.U(nXacts.W)) 79 | val xactOnehot = PriorityEncoderOH(~xactBusy) 80 | val xactId = OHToUInt(xactOnehot) 81 | val xactLast = Reg(UInt(nXacts.W)) 82 | val xactLeftKeep = Reg(Vec(nXacts, UInt(beatBytes.W))) 83 | val xactRightKeep = Reg(Vec(nXacts, UInt(beatBytes.W))) 84 | 85 | val reqSize = MuxCase(byteAddrBits.U, 86 | (log2Ceil(maxBytes) until byteAddrBits by -1).map(lgSize => 87 | // Use the largest size (beatBytes <= size <= maxBytes) 88 | // s.t. sendaddr % size == 0 and sendlen > size 89 | (sendaddr(lgSize-1,0) === 0.U && 90 | (sendlen >> lgSize.U) =/= 0.U) -> lgSize.U)) 91 | val isLast = (xactLast >> tl.d.bits.source)(0) && edge.last(tl.d) 92 | val canSend = state === s_read && !xactBusy.andR 93 | 94 | val fullKeep = ~0.U(beatBytes.W) 95 | val loffset = Reg(UInt(byteAddrBits.W)) 96 | val roffset = Reg(UInt(byteAddrBits.W)) 97 | val lkeep = fullKeep << loffset 98 | val rkeep = fullKeep >> roffset 99 | val first = Reg(Bool()) 100 | 101 | xactBusy := (xactBusy | Mux(tl.a.fire, xactOnehot, 0.U)) & 102 | ~Mux(tl.d.fire && edge.last(tl.d), 103 | UIntToOH(tl.d.bits.source), 0.U) 104 | 105 | val helper = DecoupledHelper(tl.a.ready, io.alloc.ready) 106 | 107 | io.req.ready := state === s_idle 108 | io.alloc.valid := helper.fire(io.alloc.ready, canSend) 109 | io.alloc.bits.id := xactId 110 | io.alloc.bits.count := (1.U << (reqSize - byteAddrBits.U)) 111 | tl.a.valid := helper.fire(tl.a.ready, canSend) 112 | tl.a.bits := edge.Get( 113 | fromSource = xactId, 114 | toAddress = sendaddr, 115 | lgSize = reqSize)._2 116 | 117 | val outLeftKeep = xactLeftKeep(tl.d.bits.source) 118 | val outRightKeep = xactRightKeep(tl.d.bits.source) 119 | 120 | io.out.valid := tl.d.valid 121 | io.out.bits.id := tl.d.bits.source 122 | io.out.bits.data.data := tl.d.bits.data 123 | io.out.bits.data.keep := MuxCase(fullKeep, Seq( 124 | (edge.first(tl.d) && edge.last(tl.d)) -> (outLeftKeep & outRightKeep), 125 | edge.first(tl.d) -> outLeftKeep, 126 | edge.last(tl.d) -> outRightKeep)) 127 | io.out.bits.data.last := isLast 128 | tl.d.ready := io.out.ready 129 | io.resp.valid := state === s_resp 130 | io.resp.bits := true.B 131 | 132 | when (io.req.fire) { 133 | val req = io.req.bits 134 | val lastaddr = req.address + req.length 135 | val startword = req.address(addrBits-1, byteAddrBits) 136 | val endword = lastaddr(addrBits-1, byteAddrBits) + 137 | Mux(lastaddr(byteAddrBits-1, 0) === 0.U, 0.U, 1.U) 138 | 139 | loffset := req.address(byteAddrBits-1, 0) 140 | roffset := Cat(endword, 0.U(byteAddrBits.W)) - lastaddr 141 | first := true.B 142 | 143 | sendaddr := Cat(startword, 0.U(byteAddrBits.W)) 144 | sendlen := Cat(endword - startword, 0.U(byteAddrBits.W)) 145 | sendpart := req.partial 146 | state := s_read 147 | 148 | assert(req.length > 0.U, "request length must be >0") 149 | } 150 | 151 | when (tl.a.fire) { 152 | val reqBytes = 1.U << reqSize 153 | sendaddr := sendaddr + reqBytes 154 | sendlen := sendlen - reqBytes 155 | when (sendlen === reqBytes) { 156 | xactLast := (xactLast & ~xactOnehot) | Mux(sendpart, 0.U, xactOnehot) 157 | xactRightKeep(xactId) := rkeep 158 | state := s_resp 159 | } .otherwise { 160 | xactLast := xactLast & ~xactOnehot 161 | xactRightKeep(xactId) := fullKeep 162 | } 163 | when (first) { 164 | first := false.B 165 | xactLeftKeep(xactId) := lkeep 166 | } .otherwise { 167 | xactLeftKeep(xactId) := fullKeep 168 | } 169 | } 170 | 171 | when (io.resp.fire) { 172 | state := s_idle 173 | } 174 | } 175 | } 176 | 177 | class StreamWriteRequest extends Bundle { 178 | val address = UInt(48.W) 179 | val length = UInt(16.W) 180 | } 181 | 182 | class StreamWriter(nXacts: Int, maxBytes: Int) 183 | (implicit p: Parameters) extends LazyModule { 184 | val node = TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLClientParameters( 185 | name = "stream-writer", sourceId = IdRange(0, nXacts)))))) 186 | 187 | lazy val module = new Impl 188 | class Impl extends LazyModuleImp(this) { 189 | val (tl, edge) = node.out(0) 190 | val dataBits = tl.params.dataBits 191 | val beatBytes = dataBits / 8 192 | val byteAddrBits = log2Ceil(beatBytes) 193 | val addrBits = tl.params.addressBits 194 | val lenBits = 16 195 | 196 | require (edge.manager.minLatency > 0) 197 | 198 | val io = IO(new Bundle { 199 | val req = Flipped(Decoupled(new StreamWriteRequest)) 200 | val resp = Decoupled(UInt(lenBits.W)) 201 | val in = Flipped(Decoupled(new StreamChannel(dataBits))) 202 | }) 203 | 204 | val s_idle :: s_data :: s_resp :: Nil = Enum(3) 205 | val state = RegInit(s_idle) 206 | 207 | val length = Reg(UInt(lenBits.W)) 208 | val baseAddr = Reg(UInt(addrBits.W)) 209 | val offset = Reg(UInt(addrBits.W)) 210 | val addrMerged = baseAddr + offset 211 | val bytesToSend = length - offset 212 | val baseByteOff = baseAddr(byteAddrBits-1, 0) 213 | val byteOff = addrMerged(byteAddrBits-1, 0) 214 | val extraBytes = Mux(baseByteOff === 0.U, 0.U, beatBytes.U - baseByteOff) 215 | 216 | val xactBusy = RegInit(0.U(nXacts.W)) 217 | val xactOnehot = PriorityEncoderOH(~xactBusy) 218 | val xactId = OHToUInt(xactOnehot) 219 | 220 | val maxBeats = maxBytes / beatBytes 221 | val beatIdBits = log2Ceil(maxBeats) 222 | 223 | val beatsLeft = Reg(UInt(beatIdBits.W)) 224 | val headAddr = Reg(UInt(addrBits.W)) 225 | val headXact = Reg(UInt(log2Ceil(nXacts).W)) 226 | val headSize = Reg(UInt(log2Ceil(maxBytes + 1).W)) 227 | 228 | val newBlock = beatsLeft === 0.U 229 | val canSend = !xactBusy.andR || !newBlock 230 | 231 | val reqSize = MuxCase(0.U, 232 | (log2Ceil(maxBytes) until 0 by -1).map(lgSize => 233 | (addrMerged(lgSize-1,0) === 0.U && 234 | (bytesToSend >> lgSize.U) =/= 0.U) -> lgSize.U)) 235 | 236 | xactBusy := (xactBusy | Mux(tl.a.fire && newBlock, xactOnehot, 0.U)) & 237 | ~Mux(tl.d.fire, UIntToOH(tl.d.bits.source), 0.U) 238 | 239 | val overhang = RegInit(0.U(dataBits.W)) 240 | val sendTrail = bytesToSend <= extraBytes 241 | val fulldata = (overhang | (io.in.bits.data << Cat(baseByteOff, 0.U(3.W)))) 242 | 243 | val fromSource = Mux(newBlock, xactId, headXact) 244 | val toAddress = Mux(newBlock, addrMerged, headAddr) 245 | val lgSize = Mux(newBlock, reqSize, headSize) 246 | val wdata = fulldata(dataBits-1, 0) 247 | val wmask = Cat((0 until beatBytes).map( 248 | i => (i.U >= byteOff) && (i.U < bytesToSend)).reverse) 249 | val wpartial = !wmask.andR 250 | 251 | val putPartial = edge.Put( 252 | fromSource = xactId, 253 | toAddress = addrMerged & ~(beatBytes-1).U(addrBits.W), 254 | lgSize = log2Ceil(beatBytes).U, 255 | data = Mux(sendTrail, overhang, wdata), 256 | mask = wmask)._2 257 | 258 | val putFull = edge.Put( 259 | fromSource = fromSource, 260 | toAddress = toAddress, 261 | lgSize = lgSize, 262 | data = wdata)._2 263 | 264 | io.req.ready := state === s_idle 265 | tl.a.valid := (state === s_data) && (io.in.valid || sendTrail) && canSend 266 | tl.a.bits := Mux(wpartial, putPartial, putFull) 267 | tl.d.ready := xactBusy.orR 268 | io.in.ready := state === s_data && canSend && !sendTrail && tl.a.ready 269 | io.resp.valid := state === s_resp && !xactBusy.orR 270 | io.resp.bits := length 271 | 272 | when (io.req.fire) { 273 | offset := 0.U 274 | baseAddr := io.req.bits.address 275 | length := io.req.bits.length 276 | beatsLeft := 0.U 277 | state := s_data 278 | } 279 | 280 | when (tl.a.fire) { 281 | when (!newBlock) { 282 | beatsLeft := beatsLeft - 1.U 283 | } .elsewhen (reqSize > byteAddrBits.U) { 284 | val nBeats = 1.U << (reqSize - byteAddrBits.U) 285 | beatsLeft := nBeats - 1.U 286 | headAddr := addrMerged 287 | headXact := xactId 288 | headSize := reqSize 289 | } 290 | 291 | val bytesSent = PopCount(wmask) 292 | offset := offset + bytesSent 293 | overhang := fulldata >> dataBits.U 294 | 295 | when (bytesSent === bytesToSend) { state := s_resp } 296 | } 297 | 298 | when (io.resp.fire) { state := s_idle } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/main/scala/Headers.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import IceNetConsts._ 6 | 7 | object NetworkHelpers { 8 | def reverse_bytes(a: UInt, n: Int) = { 9 | val bytes = (0 until n).map(i => a((i + 1) * 8 - 1, i * 8)) 10 | Cat(bytes) 11 | } 12 | def htonl(a: UInt) = reverse_bytes(a, 4) 13 | def ntohl(a: UInt) = reverse_bytes(a, 4) 14 | def htons(a: UInt) = reverse_bytes(a, 2) 15 | def ntohs(a: UInt) = reverse_bytes(a, 2) 16 | } 17 | 18 | class EthernetHeader extends Bundle { 19 | val ethType = UInt(ETH_TYPE_BITS.W) 20 | val srcmac = UInt(ETH_MAC_BITS.W) 21 | val dstmac = UInt(ETH_MAC_BITS.W) 22 | val padding = UInt(ETH_PAD_BITS.W) 23 | 24 | def toWords(w: Int = NET_IF_WIDTH) = 25 | this.asTypeOf(Vec(ETH_HEAD_BYTES * 8 / w, UInt(w.W))) 26 | } 27 | 28 | object EthernetHeader { 29 | def apply(dstmac: UInt, srcmac: UInt, ethType: UInt) = { 30 | val header = Wire(new EthernetHeader) 31 | header.dstmac := dstmac 32 | header.srcmac := srcmac 33 | header.ethType := ethType 34 | header.padding := DontCare 35 | header 36 | } 37 | 38 | def apply(words: Seq[UInt], w: Int = NET_IF_WIDTH) = { 39 | val headerWords = ETH_HEAD_BYTES * 8 / w 40 | Cat(words.take(headerWords).reverse).asTypeOf(new EthernetHeader) 41 | } 42 | } 43 | 44 | class IPv4Header extends Bundle { 45 | val dest_ip = UInt(32.W) 46 | val source_ip = UInt(32.W) 47 | val header_checksum = UInt(16.W) 48 | val protocol = UInt(8.W) 49 | val ttl = UInt(8.W) 50 | val frag_off2 = UInt(8.W) 51 | val flags = UInt(3.W) 52 | val frag_off1 = UInt(5.W) 53 | val ident = UInt(16.W) 54 | val total_length = UInt(16.W) 55 | val dcsp = UInt(6.W) 56 | val ecn = UInt(2.W) 57 | val version = UInt(4.W) 58 | val ihl = UInt(4.W) 59 | } 60 | 61 | class UDPHeader extends Bundle { 62 | val checksum = UInt(16.W) 63 | val length = UInt(16.W) 64 | val dest_port = UInt(16.W) 65 | val source_port = UInt(16.W) 66 | } 67 | 68 | class TCPHeader extends Bundle { 69 | val urgent_pointer = UInt(16.W) 70 | val checksum = UInt(16.W) 71 | val window_size = UInt(16.W) 72 | val cwr = Bool() 73 | val ece = Bool() 74 | val urg = Bool() 75 | val ack = Bool() 76 | val psh = Bool() 77 | val rst = Bool() 78 | val syn = Bool() 79 | val fin = Bool() 80 | val data_offset = UInt(4.W) 81 | val reserved = UInt(3.W) 82 | val ns = Bool() 83 | val acknum = UInt(32.W) 84 | val seqnum = UInt(32.W) 85 | val dest_port = UInt(16.W) 86 | val source_port = UInt(16.W) 87 | } 88 | -------------------------------------------------------------------------------- /src/main/scala/Limiter.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.unittest.UnitTest 6 | import IceNetConsts._ 7 | 8 | class RateLimiterSettings extends Bundle { 9 | val incBits = log2Ceil(RLIMIT_MAX_INC) 10 | val periodBits = log2Ceil(RLIMIT_MAX_PERIOD) 11 | val sizeBits = log2Ceil(RLIMIT_MAX_SIZE) 12 | 13 | /* 14 | * Given a clock frequency of X, you can achieve an output bandwidth 15 | * of Y = X * (N / D), where N <= D, by setting inc to N and period to (D - 1). 16 | * The field size should be set to the number of consecutive beats that 17 | * can be sent before rate-limiting kicks in. 18 | */ 19 | val inc = UInt(incBits.W) 20 | val period = UInt(periodBits.W) 21 | val size = UInt(sizeBits.W) 22 | } 23 | 24 | class RateLimiter[T <: Data](typ: T) extends Module { 25 | val incBits = log2Ceil(RLIMIT_MAX_INC) 26 | val periodBits = log2Ceil(RLIMIT_MAX_PERIOD) 27 | val sizeBits = log2Ceil(RLIMIT_MAX_SIZE) 28 | 29 | val io = IO(new Bundle { 30 | val in = Flipped(Decoupled(typ)) 31 | val out = Decoupled(typ) 32 | val settings = Input(new RateLimiterSettings) 33 | }) 34 | 35 | val tokens = RegInit(0.U(sizeBits.W)) 36 | val counter = RegInit(0.U(periodBits.W)) 37 | 38 | when (counter === 0.U) { 39 | counter := io.settings.period 40 | } .otherwise { 41 | counter := counter - 1.U 42 | } 43 | 44 | val inc_trigger = counter === 0.U 45 | val ok_to_send = tokens > 0.U 46 | 47 | io.in.ready := io.out.ready && ok_to_send 48 | io.out.valid := io.in.valid && ok_to_send 49 | io.out.bits := io.in.bits 50 | 51 | def uint_min(a: UInt, b: UInt) = Mux(a < b, a, b) 52 | 53 | when (inc_trigger && io.out.fire) { 54 | val next_tokens = tokens +& (io.settings.inc - 1.U) 55 | tokens := uint_min(next_tokens, io.settings.size) 56 | } .elsewhen (inc_trigger) { 57 | val next_tokens = tokens +& io.settings.inc 58 | tokens := uint_min(next_tokens, io.settings.size) 59 | } .elsewhen (io.out.fire) { 60 | tokens := tokens - 1.U 61 | } 62 | } 63 | 64 | object RateLimiter { 65 | def apply[T <: Data](in: DecoupledIO[T], inc: Int, period: Int, size: Int) = { 66 | if (period > 1) { 67 | val limiter = Module(new RateLimiter(in.bits.cloneType)) 68 | limiter.io.in <> in 69 | limiter.io.settings.inc := inc.U 70 | limiter.io.settings.period := (period - 1).U 71 | limiter.io.settings.size := size.U 72 | limiter.io.out 73 | } else { in } 74 | } 75 | } 76 | 77 | class RateLimiterTest extends UnitTest { 78 | val nFlits = 48 79 | val started = RegInit(false.B) 80 | val sending = RegInit(false.B) 81 | val receiving = RegInit(false.B) 82 | 83 | val limiter = Module(new RateLimiter(Bool())) 84 | limiter.io.in.valid := sending 85 | limiter.io.in.bits := true.B 86 | limiter.io.out.ready := receiving 87 | limiter.io.settings.inc := 1.U 88 | limiter.io.settings.period := 3.U 89 | limiter.io.settings.size := 2.U 90 | 91 | val (sendCount, sendDone) = Counter(limiter.io.in.fire, nFlits) 92 | val (recvCount, recvDone) = Counter(limiter.io.out.fire, nFlits) 93 | 94 | when (io.start && !started) { 95 | started := true.B 96 | sending := true.B 97 | receiving := true.B 98 | } 99 | 100 | when (sendDone) { sending := false.B } 101 | when (recvDone) { receiving := false.B } 102 | 103 | io.finished := started && !sending && !receiving 104 | } 105 | -------------------------------------------------------------------------------- /src/main/scala/NIC.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import chisel3.reflect.DataMirror 6 | import freechips.rocketchip.subsystem.{BaseSubsystem, TLBusWrapperLocation, PBUS, FBUS} 7 | import org.chipsalliance.cde.config.{Field, Parameters} 8 | import freechips.rocketchip.diplomacy._ 9 | import freechips.rocketchip.prci._ 10 | import freechips.rocketchip.regmapper._ 11 | import freechips.rocketchip.interrupts._ 12 | import freechips.rocketchip.tilelink._ 13 | import freechips.rocketchip.util._ 14 | import IceNetConsts._ 15 | 16 | // This is copied from testchipip to avoid dependencies 17 | class ClockedIO[T <: Data](private val gen: T) extends Bundle { 18 | val clock = Output(Clock()) 19 | val bits = DataMirror.internal.chiselTypeClone[T](gen) 20 | } 21 | 22 | /** 23 | * @inBufFlits How many flits in the input buffer(s) 24 | * @outBufFlits Number of flits in the output buffer 25 | * @nMemXacts Maximum number of transactions that the send/receive path can send to memory 26 | * @maxAcquireBytes Cache block size 27 | * @ctrlQueueDepth Depth of the MMIO control queues 28 | * @usePauser Hardware support for Ethernet pause frames 29 | * @checksumOffload TCP checksum offload engine 30 | * @packetMaxBytes Maximum number of bytes in a packet (header size + MTU) 31 | */ 32 | case class NICConfig( 33 | inBufFlits: Int = 2 * ETH_STANDARD_MAX_BYTES / NET_IF_BYTES, 34 | outBufFlits: Int = 2 * ETH_STANDARD_MAX_BYTES / NET_IF_BYTES, 35 | nMemXacts: Int = 8, 36 | maxAcquireBytes: Int = 64, 37 | ctrlQueueDepth: Int = 10, 38 | usePauser: Boolean = false, 39 | checksumOffload: Boolean = false, 40 | packetMaxBytes: Int = ETH_STANDARD_MAX_BYTES) 41 | 42 | case class NICAttachParams( 43 | masterWhere: TLBusWrapperLocation = FBUS, 44 | slaveWhere: TLBusWrapperLocation = PBUS 45 | ) 46 | 47 | case object NICKey extends Field[Option[NICConfig]](None) 48 | case object NICAttachKey extends Field[NICAttachParams](NICAttachParams()) 49 | 50 | trait HasNICParameters { 51 | implicit val p: Parameters 52 | val nicExternal = p(NICKey).get 53 | val inBufFlits = nicExternal.inBufFlits 54 | val outBufFlits = nicExternal.outBufFlits 55 | val nMemXacts = nicExternal.nMemXacts 56 | val maxAcquireBytes = nicExternal.maxAcquireBytes 57 | val ctrlQueueDepth = nicExternal.ctrlQueueDepth 58 | val usePauser = nicExternal.usePauser 59 | val checksumOffload = nicExternal.checksumOffload 60 | val packetMaxBytes = nicExternal.packetMaxBytes 61 | } 62 | 63 | abstract class NICLazyModule(implicit p: Parameters) 64 | extends LazyModule with HasNICParameters 65 | 66 | abstract class NICModule(implicit val p: Parameters) 67 | extends Module with HasNICParameters 68 | 69 | abstract class NICBundle(implicit val p: Parameters) 70 | extends Bundle with HasNICParameters 71 | 72 | class PacketArbiter(arbN: Int, rr: Boolean = false) 73 | extends HellaPeekingArbiter( 74 | new StreamChannel(NET_IF_WIDTH), arbN, 75 | (ch: StreamChannel) => ch.last, rr = rr) 76 | 77 | class IceNicSendIO extends Bundle { 78 | val req = Decoupled(UInt(NET_IF_WIDTH.W)) 79 | val comp = Flipped(Decoupled(Bool())) 80 | } 81 | 82 | class IceNicRecvIO extends Bundle { 83 | val req = Decoupled(UInt(NET_IF_WIDTH.W)) 84 | val comp = Flipped(Decoupled(UInt(NET_LEN_BITS.W))) 85 | } 86 | 87 | trait IceNicControllerBundle extends Bundle { 88 | val send = new IceNicSendIO 89 | val recv = new IceNicRecvIO 90 | val macAddr = Input(UInt(ETH_MAC_BITS.W)) 91 | val txcsumReq = Decoupled(new ChecksumRewriteRequest) 92 | val rxcsumRes = Flipped(Decoupled(new TCPChecksumOffloadResult)) 93 | val csumEnable = Output(Bool()) 94 | } 95 | 96 | case class IceNicControllerParams(address: BigInt, beatBytes: Int) 97 | 98 | /* 99 | * Take commands from the CPU over TL2, expose as Queues 100 | */ 101 | class IceNicController(c: IceNicControllerParams)(implicit p: Parameters) 102 | extends RegisterRouter(RegisterRouterParams("ice-nic", Seq("ucb-bar,ice-nic"), 103 | c.address, beatBytes=c.beatBytes)) 104 | with HasTLControlRegMap 105 | with HasInterruptSources 106 | with HasNICParameters { 107 | override def nInterrupts = 2 108 | def tlRegmap(mapping: RegField.Map*): Unit = regmap(mapping:_*) 109 | override lazy val module = new IceNiCControllerModuleImp(this) 110 | } 111 | 112 | class IceNiCControllerModuleImp(outer: IceNicController)(implicit p: Parameters) extends LazyModuleImp(outer) with HasNICParameters { 113 | val io = IO(new Bundle with IceNicControllerBundle) 114 | 115 | val sendCompDown = WireInit(false.B) 116 | 117 | val qDepth = ctrlQueueDepth 118 | require(qDepth < (1 << 8)) 119 | 120 | def queueCount[T <: Data](qio: QueueIO[T], depth: Int): UInt = 121 | TwoWayCounter(qio.enq.fire, qio.deq.fire, depth) 122 | 123 | // hold (len, addr) of packets that we need to send out 124 | val sendReqQueue = Module(new HellaQueue(qDepth)(UInt(NET_IF_WIDTH.W))) 125 | val sendReqCount = queueCount(sendReqQueue.io, qDepth) 126 | // hold addr of buffers we can write received packets into 127 | val recvReqQueue = Module(new HellaQueue(qDepth)(UInt(NET_IF_WIDTH.W))) 128 | val recvReqCount = queueCount(recvReqQueue.io, qDepth) 129 | // count number of sends completed 130 | val sendCompCount = TwoWayCounter(io.send.comp.fire, sendCompDown, qDepth) 131 | // hold length of received packets 132 | val recvCompQueue = Module(new HellaQueue(qDepth)(UInt(NET_LEN_BITS.W))) 133 | val recvCompCount = queueCount(recvCompQueue.io, qDepth) 134 | 135 | val sendCompValid = sendCompCount > 0.U 136 | val intMask = RegInit(0.U(2.W)) 137 | 138 | io.send.req <> sendReqQueue.io.deq 139 | io.recv.req <> recvReqQueue.io.deq 140 | io.send.comp.ready := sendCompCount < qDepth.U 141 | recvCompQueue.io.enq <> io.recv.comp 142 | 143 | outer.interrupts(0) := sendCompValid && intMask(0) 144 | outer.interrupts(1) := recvCompQueue.io.deq.valid && intMask(1) 145 | 146 | val sendReqSpace = (qDepth.U - sendReqCount) 147 | val recvReqSpace = (qDepth.U - recvReqCount) 148 | 149 | def sendCompRead = (ready: Bool) => { 150 | sendCompDown := sendCompValid && ready 151 | (sendCompValid, true.B) 152 | } 153 | 154 | val txcsumReqQueue = Module(new HellaQueue(qDepth)(UInt(49.W))) 155 | val rxcsumResQueue = Module(new HellaQueue(qDepth)(UInt(2.W))) 156 | val csumEnable = RegInit(false.B) 157 | 158 | io.txcsumReq.valid := txcsumReqQueue.io.deq.valid 159 | io.txcsumReq.bits := txcsumReqQueue.io.deq.bits.asTypeOf(new ChecksumRewriteRequest) 160 | txcsumReqQueue.io.deq.ready := io.txcsumReq.ready 161 | 162 | rxcsumResQueue.io.enq.valid := io.rxcsumRes.valid 163 | rxcsumResQueue.io.enq.bits := io.rxcsumRes.bits.asUInt 164 | io.rxcsumRes.ready := rxcsumResQueue.io.enq.ready 165 | 166 | io.csumEnable := csumEnable 167 | 168 | outer.tlRegmap( 169 | 0x00 -> Seq(RegField.w(NET_IF_WIDTH, sendReqQueue.io.enq)), 170 | 0x08 -> Seq(RegField.w(NET_IF_WIDTH, recvReqQueue.io.enq)), 171 | 0x10 -> Seq(RegField.r(1, sendCompRead)), 172 | 0x12 -> Seq(RegField.r(NET_LEN_BITS, recvCompQueue.io.deq)), 173 | 0x14 -> Seq( 174 | RegField.r(8, sendReqSpace), 175 | RegField.r(8, recvReqSpace), 176 | RegField.r(8, sendCompCount), 177 | RegField.r(8, recvCompCount)), 178 | 0x18 -> Seq(RegField.r(ETH_MAC_BITS, io.macAddr)), 179 | 0x20 -> Seq(RegField(2, intMask)), 180 | 0x28 -> Seq(RegField.w(49, txcsumReqQueue.io.enq)), 181 | 0x30 -> Seq(RegField.r(2, rxcsumResQueue.io.deq)), 182 | 0x31 -> Seq(RegField(1, csumEnable))) 183 | } 184 | 185 | class IceNicSendPath(nInputTaps: Int = 0)(implicit p: Parameters) 186 | extends NICLazyModule { 187 | val reader = LazyModule(new StreamReader( 188 | nMemXacts, outBufFlits, maxAcquireBytes)) 189 | val node = reader.node 190 | 191 | lazy val module = new Impl 192 | class Impl extends LazyModuleImp(this) { 193 | val io = IO(new Bundle { 194 | val send = Flipped(new IceNicSendIO) 195 | val tap = Flipped(Vec(nInputTaps, Decoupled(new StreamChannel(NET_IF_WIDTH)))) 196 | val out = Decoupled(new StreamChannel(NET_IF_WIDTH)) 197 | val rlimit = Input(new RateLimiterSettings) 198 | val csum = checksumOffload.option(new Bundle { 199 | val req = Flipped(Decoupled(new ChecksumRewriteRequest)) 200 | val enable = Input(Bool()) 201 | }) 202 | }) 203 | 204 | val readreq = reader.module.io.req 205 | io.send.req.ready := readreq.ready 206 | readreq.valid := io.send.req.valid 207 | readreq.bits.address := io.send.req.bits(47, 0) 208 | readreq.bits.length := io.send.req.bits(62, 48) 209 | readreq.bits.partial := io.send.req.bits(63) 210 | io.send.comp <> reader.module.io.resp 211 | 212 | val preArbOut = if (checksumOffload) { 213 | val readerOut = reader.module.io.out 214 | val arb = Module(new PacketArbiter(2)) 215 | val bufFlits = (packetMaxBytes - 1) / NET_IF_BYTES + 1 216 | val rewriter = Module(new ChecksumRewrite(NET_IF_WIDTH, bufFlits)) 217 | val enable = io.csum.get.enable 218 | 219 | rewriter.io.req <> io.csum.get.req 220 | 221 | arb.io.in(0) <> rewriter.io.stream.out 222 | arb.io.in(1).valid := !enable && readerOut.valid 223 | arb.io.in(1).bits := readerOut.bits 224 | rewriter.io.stream.in.valid := enable && readerOut.valid 225 | rewriter.io.stream.in.bits := readerOut.bits 226 | readerOut.ready := Mux(enable, 227 | rewriter.io.stream.in.ready, arb.io.in(1).ready) 228 | 229 | arb.io.out 230 | } else { reader.module.io.out } 231 | 232 | val unlimitedOut = if (nInputTaps > 0) { 233 | val bufWords = (packetMaxBytes - 1) / NET_IF_BYTES + 1 234 | val inputs = (preArbOut +: io.tap).map { in => 235 | // The packet collection buffer doesn't allow sending the first flit 236 | // of a packet until the last flit is received. 237 | // This ensures that we don't lock the arbiter while waiting for data 238 | // to arrive, which could cause deadocks. 239 | val buffer = Module(new PacketCollectionBuffer(bufWords)) 240 | buffer.io.in <> in 241 | buffer.io.out 242 | } 243 | val arb = Module(new PacketArbiter(inputs.size, rr = true)) 244 | arb.io.in <> inputs 245 | arb.io.out 246 | } else { preArbOut } 247 | 248 | val limiter = Module(new RateLimiter(new StreamChannel(NET_IF_WIDTH))) 249 | limiter.io.in <> unlimitedOut 250 | limiter.io.settings := io.rlimit 251 | io.out <> limiter.io.out 252 | } 253 | } 254 | 255 | class IceNicWriter(implicit p: Parameters) extends NICLazyModule { 256 | val writer = LazyModule(new StreamWriter(nMemXacts, maxAcquireBytes)) 257 | val node = writer.node 258 | 259 | lazy val module = new Impl 260 | class Impl extends LazyModuleImp(this) { 261 | val io = IO(new Bundle { 262 | val recv = Flipped(new IceNicRecvIO) 263 | val in = Flipped(Decoupled(new StreamChannel(NET_IF_WIDTH))) 264 | val length = Flipped(Valid(UInt(NET_LEN_BITS.W))) 265 | }) 266 | 267 | val streaming = RegInit(false.B) 268 | val byteAddrBits = log2Ceil(NET_IF_BYTES) 269 | val helper = DecoupledHelper( 270 | io.recv.req.valid, 271 | writer.module.io.req.ready, 272 | io.length.valid, !streaming) 273 | 274 | writer.module.io.req.valid := helper.fire(writer.module.io.req.ready) 275 | writer.module.io.req.bits.address := io.recv.req.bits 276 | writer.module.io.req.bits.length := io.length.bits 277 | io.recv.req.ready := helper.fire(io.recv.req.valid) 278 | 279 | writer.module.io.in.valid := io.in.valid && streaming 280 | writer.module.io.in.bits := io.in.bits 281 | io.in.ready := writer.module.io.in.ready && streaming 282 | 283 | io.recv.comp <> writer.module.io.resp 284 | 285 | when (io.recv.req.fire) { streaming := true.B } 286 | when (io.in.fire && io.in.bits.last) { streaming := false.B } 287 | } 288 | } 289 | 290 | /* 291 | * Recv frames 292 | */ 293 | class IceNicRecvPath(val tapFuncs: Seq[EthernetHeader => Bool] = Nil) 294 | (implicit p: Parameters) extends LazyModule { 295 | val writer = LazyModule(new IceNicWriter) 296 | val node = TLIdentityNode() 297 | node := writer.node 298 | lazy val module = new IceNicRecvPathModule(this) 299 | } 300 | 301 | class IceNicRecvPathModule(val outer: IceNicRecvPath) 302 | extends LazyModuleImp(outer) with HasNICParameters { 303 | val io = IO(new Bundle { 304 | val recv = Flipped(new IceNicRecvIO) 305 | val in = Flipped(Decoupled(new StreamChannel(NET_IF_WIDTH))) // input stream 306 | val tap = Vec(outer.tapFuncs.length, Decoupled(new StreamChannel(NET_IF_WIDTH))) 307 | val csum = checksumOffload.option(new Bundle { 308 | val res = Decoupled(new TCPChecksumOffloadResult) 309 | val enable = Input(Bool()) 310 | }) 311 | val buf_free = Output(Vec(1 + outer.tapFuncs.length, UInt(8.W))) 312 | }) 313 | 314 | def tapOutToDropCheck(tapOut: EthernetHeader => Bool) = { 315 | (header: EthernetHeader, ch: StreamChannel, update: Bool) => { 316 | val first = RegInit(true.B) 317 | val drop = tapOut(header) && first 318 | val dropReg = RegInit(false.B) 319 | 320 | when (update && first) { first := false.B; dropReg := drop } 321 | when (update && ch.last) { first := true.B; dropReg := false.B } 322 | 323 | drop || dropReg 324 | } 325 | } 326 | 327 | def duplicateStream(in: DecoupledIO[StreamChannel], outs: Seq[DecoupledIO[StreamChannel]]) = { 328 | outs.foreach { out => 329 | out.valid := in.valid 330 | out.bits := in.bits 331 | } 332 | in.ready := outs.head.ready 333 | val outReadys = Cat(outs.map(_.ready)) 334 | assert(outReadys.andR || !outReadys.orR, 335 | "Duplicated streams must all be ready simultaneously") 336 | outs 337 | } 338 | 339 | def invertCheck(check: (EthernetHeader, StreamChannel, Bool) => Bool) = 340 | (eth: EthernetHeader, ch: StreamChannel, up: Bool) => !check(eth, ch, up) 341 | 342 | val tapDropChecks = outer.tapFuncs.map(func => tapOutToDropCheck(func)) 343 | val pauseDropCheck = if (usePauser) Some(PauseDropCheck(_, _, _)) else None 344 | val allDropChecks = 345 | // Drop checks for the primary buffer 346 | // Drop if the packet should be tapped out or is a pause frame 347 | Seq(tapDropChecks ++ pauseDropCheck.toSeq) ++ 348 | // Drop checks for the tap buffers 349 | // For each tap, drop if the packet doesn't match the tap function or is a pause frame 350 | tapDropChecks.map(check => invertCheck(check) +: pauseDropCheck.toSeq) 351 | 352 | val buffers = allDropChecks.map(dropChecks => 353 | Module(new NetworkPacketBuffer( 354 | inBufFlits, 355 | maxBytes = packetMaxBytes, 356 | dropChecks = dropChecks, dropless = usePauser))) 357 | duplicateStream(io.in, buffers.map(_.io.stream.in)) 358 | 359 | io.buf_free := buffers.map(_.io.free) 360 | 361 | io.tap <> buffers.tail.map(_.io.stream.out) 362 | val bufout = buffers.head.io.stream.out 363 | val buflen = buffers.head.io.length 364 | 365 | val (csumout, recvreq) = (if (checksumOffload) { 366 | val offload = Module(new TCPChecksumOffload(NET_IF_WIDTH)) 367 | val offloadReady = offload.io.in.ready || !io.csum.get.enable 368 | 369 | val out = Wire(Decoupled(new StreamChannel(NET_IF_WIDTH))) 370 | val recvreq = Wire(Decoupled(UInt(NET_IF_WIDTH.W))) 371 | val reqq = Module(new Queue(UInt(NET_IF_WIDTH.W), 1)) 372 | 373 | val enqHelper = DecoupledHelper( 374 | io.recv.req.valid, reqq.io.enq.ready, recvreq.ready) 375 | val deqHelper = DecoupledHelper( 376 | bufout.valid, offloadReady, out.ready, reqq.io.deq.valid) 377 | 378 | reqq.io.enq.valid := enqHelper.fire(reqq.io.enq.ready) 379 | reqq.io.enq.bits := io.recv.req.bits 380 | io.recv.req.ready := enqHelper.fire(io.recv.req.valid) 381 | recvreq.valid := enqHelper.fire(recvreq.ready) 382 | recvreq.bits := io.recv.req.bits 383 | 384 | out.valid := deqHelper.fire(out.ready) 385 | out.bits := bufout.bits 386 | offload.io.in.valid := deqHelper.fire(offloadReady, io.csum.get.enable) 387 | offload.io.in.bits := bufout.bits 388 | bufout.ready := deqHelper.fire(bufout.valid) 389 | reqq.io.deq.ready := deqHelper.fire(reqq.io.deq.valid, bufout.bits.last) 390 | 391 | io.csum.get.res <> offload.io.result 392 | 393 | (out, recvreq) 394 | } else { (bufout, io.recv.req) }) 395 | 396 | val writer = outer.writer.module 397 | writer.io.recv.req <> Queue(recvreq, 1) 398 | io.recv.comp <> writer.io.recv.comp 399 | writer.io.in <> csumout 400 | writer.io.length.valid := buflen.valid 401 | writer.io.length.bits := buflen.bits 402 | } 403 | 404 | class NICIO extends StreamIO(NET_IF_WIDTH) { 405 | val macAddr = Input(UInt(ETH_MAC_BITS.W)) 406 | val rlimit = Input(new RateLimiterSettings) 407 | val pauser = Input(new PauserSettings) 408 | 409 | } 410 | 411 | /* 412 | * A simple NIC 413 | * 414 | * Expects ethernet frames (see below), but uses a custom transport 415 | * (see ExtBundle) 416 | * 417 | * Ethernet Frame format: 418 | * 2 bytes | 6 bytes | 6 bytes | 2 bytes | 46-1500B 419 | * Padding | Dest Addr | Source Addr | Type/Len | Data 420 | * 421 | * @address Starting address of MMIO control registers 422 | * @beatBytes Width of memory interface (in bytes) 423 | * @tapOutFuncs Sequence of functions for each output tap. 424 | * Each function takes the header of an Ethernet frame 425 | * and returns Bool that is true if matching and false if not. 426 | * @nInputTaps Number of input taps 427 | * 428 | */ 429 | class IceNIC(address: BigInt, beatBytes: Int = 8, 430 | tapOutFuncs: Seq[EthernetHeader => Bool] = Nil, 431 | nInputTaps: Int = 0) 432 | (implicit p: Parameters) extends NICLazyModule { 433 | 434 | val control = LazyModule(new IceNicController( 435 | IceNicControllerParams(address, beatBytes))) 436 | val sendPath = LazyModule(new IceNicSendPath(nInputTaps)) 437 | val recvPath = LazyModule(new IceNicRecvPath(tapOutFuncs)) 438 | 439 | val mmionode = TLIdentityNode() 440 | val dmanode = TLIdentityNode() 441 | val intnode = control.intXing(NoCrossing) 442 | 443 | control.node := TLAtomicAutomata() := mmionode 444 | dmanode := TLWidthWidget(NET_IF_BYTES) := sendPath.node 445 | dmanode := TLWidthWidget(NET_IF_BYTES) := recvPath.node 446 | 447 | lazy val module = new Impl 448 | class Impl extends LazyModuleImp(this) { 449 | val io = IO(new Bundle { 450 | val ext = new NICIO 451 | val tapOut = Vec(tapOutFuncs.length, Decoupled(new StreamChannel(NET_IF_WIDTH))) 452 | val tapIn = Flipped(Vec(nInputTaps, Decoupled(new StreamChannel(NET_IF_WIDTH)))) 453 | }) 454 | 455 | sendPath.module.io.send <> control.module.io.send 456 | recvPath.module.io.recv <> control.module.io.recv 457 | 458 | // connect externally 459 | if (usePauser) { 460 | val pauser = Module(new Pauser(inBufFlits, 1 + tapOutFuncs.length)) 461 | pauser.io.int.out <> sendPath.module.io.out 462 | recvPath.module.io.in <> pauser.io.int.in 463 | io.ext.out <> pauser.io.ext.out 464 | pauser.io.ext.in <> io.ext.in 465 | pauser.io.in_free := recvPath.module.io.buf_free 466 | pauser.io.macAddr := io.ext.macAddr 467 | pauser.io.settings := io.ext.pauser 468 | } else { 469 | recvPath.module.io.in <> io.ext.in 470 | io.ext.out <> sendPath.module.io.out 471 | } 472 | 473 | control.module.io.macAddr := io.ext.macAddr 474 | sendPath.module.io.rlimit := io.ext.rlimit 475 | 476 | io.tapOut <> recvPath.module.io.tap 477 | sendPath.module.io.tap <> io.tapIn 478 | 479 | if (checksumOffload) { 480 | sendPath.module.io.csum.get.req <> control.module.io.txcsumReq 481 | sendPath.module.io.csum.get.enable := control.module.io.csumEnable 482 | control.module.io.rxcsumRes <> recvPath.module.io.csum.get.res 483 | recvPath.module.io.csum.get.enable := control.module.io.csumEnable 484 | } else { 485 | control.module.io.txcsumReq.ready := false.B 486 | control.module.io.rxcsumRes.valid := false.B 487 | control.module.io.rxcsumRes.bits := DontCare 488 | } 489 | } 490 | } 491 | 492 | class SimNetwork extends BlackBox with HasBlackBoxResource { 493 | val io = IO(new Bundle { 494 | val clock = Input(Clock()) 495 | val reset = Input(Bool()) 496 | val net = Flipped(new NICIOvonly) 497 | }) 498 | addResource("/vsrc/SimNetwork.v") 499 | addResource("/csrc/SimNetwork.cc") 500 | addResource("/csrc/device.h") 501 | addResource("/csrc/device.cc") 502 | addResource("/csrc/switch.h") 503 | addResource("/csrc/switch.cc") 504 | addResource("/csrc/packet.h") 505 | } 506 | 507 | 508 | class NICIOvonly extends Bundle { 509 | val in = Flipped(Valid(new StreamChannel(NET_IF_WIDTH))) 510 | val out = Valid(new StreamChannel(NET_IF_WIDTH)) 511 | val macAddr = Input(UInt(ETH_MAC_BITS.W)) 512 | val rlimit = Input(new RateLimiterSettings) 513 | val pauser = Input(new PauserSettings) 514 | 515 | } 516 | 517 | object NICIOvonly { 518 | def apply(nicio: NICIO): NICIOvonly = { 519 | val vonly = Wire(new NICIOvonly) 520 | vonly.out.valid := nicio.out.valid 521 | vonly.out.bits := nicio.out.bits 522 | nicio.out.ready := true.B 523 | nicio.in.valid := vonly.in.valid 524 | nicio.in.bits := vonly.in.bits 525 | assert(!vonly.in.valid || nicio.in.ready, "NIC input not ready for valid") 526 | nicio.macAddr := vonly.macAddr 527 | nicio.rlimit := vonly.rlimit 528 | nicio.pauser := vonly.pauser 529 | vonly 530 | } 531 | } 532 | 533 | object NICIO { 534 | def apply(vonly: NICIOvonly): NICIO = { 535 | val nicio = Wire(new NICIO) 536 | assert(!vonly.out.valid || nicio.out.ready) 537 | nicio.out.valid := vonly.out.valid 538 | nicio.out.bits := vonly.out.bits 539 | vonly.in.valid := nicio.in.valid 540 | vonly.in.bits := nicio.in.bits 541 | nicio.in.ready := true.B 542 | vonly.macAddr := nicio.macAddr 543 | vonly.rlimit := nicio.rlimit 544 | vonly.pauser := nicio.pauser 545 | nicio 546 | } 547 | 548 | } 549 | trait CanHavePeripheryIceNIC { this: BaseSubsystem => 550 | private val address = BigInt(0x10016000) 551 | private val portName = "Ice-NIC" 552 | 553 | 554 | val icenicOpt = p(NICKey).map { params => 555 | val manager = locateTLBusWrapper(p(NICAttachKey).slaveWhere) 556 | val client = locateTLBusWrapper(p(NICAttachKey).masterWhere) 557 | // TODO: currently the controller is in the clock domain of the bus which masters it 558 | // we assume this is same as the clock domain of the bus the controller masters 559 | val domain = manager.generateSynchronousDomain.suggestName("icenic_domain") 560 | 561 | val icenic = domain { LazyModule(new IceNIC(address, manager.beatBytes)) } 562 | 563 | manager.coupleTo(portName) { icenic.mmionode := TLFragmenter(manager.beatBytes, manager.blockBytes) := _ } 564 | client.coupleFrom(portName) { _ :=* icenic.dmanode } 565 | ibus.fromSync := icenic.intnode 566 | 567 | val inner_io = domain { InModuleBody { 568 | val inner_io = IO(new NICIOvonly).suggestName("nic") 569 | inner_io <> NICIOvonly(icenic.module.io.ext) 570 | inner_io 571 | } } 572 | 573 | val outer_io = InModuleBody { 574 | val outer_io = IO(new ClockedIO(new NICIOvonly)).suggestName("nic") 575 | outer_io.bits <> inner_io 576 | outer_io.clock := domain.module.clock 577 | outer_io 578 | } 579 | outer_io 580 | } 581 | } 582 | 583 | 584 | object NicLoopback { 585 | def connect(net: Option[NICIOvonly], nicConf: Option[NICConfig], qDepth: Int, latency: Int = 10): Unit = { 586 | net.foreach { netio => 587 | import PauseConsts.BT_PER_QUANTA 588 | val packetWords = nicConf.get.packetMaxBytes / NET_IF_BYTES 589 | val packetQuanta = (nicConf.get.packetMaxBytes * 8) / BT_PER_QUANTA 590 | netio.macAddr := PlusArg("macaddr") 591 | netio.rlimit.inc := PlusArg("rlimit-inc", 1) 592 | netio.rlimit.period := PlusArg("rlimit-period", 1) 593 | netio.rlimit.size := PlusArg("rlimit-size", 8) 594 | netio.pauser.threshold := PlusArg("pauser-threshold", 2 * packetWords + latency) 595 | netio.pauser.quanta := PlusArg("pauser-quanta", 2 * packetQuanta) 596 | netio.pauser.refresh := PlusArg("pauser-refresh", packetWords) 597 | 598 | if (nicConf.get.usePauser) { 599 | val pauser = Module(new PauserComplex(qDepth)) 600 | pauser.io.ext.flipConnect(NetDelay(NICIO(netio), latency)) 601 | pauser.io.int.out <> pauser.io.int.in 602 | pauser.io.macAddr := netio.macAddr + (1 << 40).U 603 | pauser.io.settings := netio.pauser 604 | } else { 605 | 606 | netio.in := Pipe(netio.out, latency) 607 | } 608 | netio.in.bits.keep := NET_FULL_KEEP 609 | } 610 | } 611 | 612 | def connect(net: NICIOvonly, nicConf: NICConfig): Unit = { 613 | val packetWords = nicConf.packetMaxBytes / NET_IF_BYTES 614 | NicLoopback.connect(Some(net), Some(nicConf), 4 * packetWords) 615 | } 616 | } 617 | 618 | object SimNetwork { 619 | def connect(net: Option[NICIOvonly], clock: Clock, reset: Bool) { 620 | net.foreach { netio => 621 | val sim = Module(new SimNetwork) 622 | sim.io.clock := clock 623 | sim.io.reset := reset 624 | sim.io.net <> netio 625 | } 626 | } 627 | } 628 | -------------------------------------------------------------------------------- /src/main/scala/NICTests.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | import org.chipsalliance.cde.config.Parameters 7 | import freechips.rocketchip.devices.tilelink.TLROM 8 | import freechips.rocketchip.diplomacy._ 9 | import freechips.rocketchip.tilelink._ 10 | import freechips.rocketchip.unittest.{UnitTest, UnitTestIO} 11 | import freechips.rocketchip.util.{LatencyPipe, TwoWayCounter, UIntIsOneOf} 12 | import scala.math.max 13 | import IceNetConsts._ 14 | 15 | class IceNicTestSendDriver( 16 | sendReqs: Seq[(Int, Int, Boolean)], 17 | sendData: Seq[BigInt])(implicit p: Parameters) extends LazyModule { 18 | val node = TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLClientParameters( 19 | name = "test-send-driver", sourceId = IdRange(0, 1)))))) 20 | 21 | lazy val module = new Impl 22 | class Impl extends LazyModuleImp(this) { 23 | val io = IO(new Bundle with UnitTestIO { 24 | val send = new IceNicSendIO 25 | }) 26 | 27 | val (tl, edge) = node.out(0) 28 | val dataBits = tl.params.dataBits 29 | val beatBytes = dataBits / 8 30 | val byteAddrBits = log2Ceil(beatBytes) 31 | val lenBits = NET_LEN_BITS - 1 32 | val addrBits = NET_IF_WIDTH - NET_LEN_BITS 33 | 34 | val (s_start :: s_write_req :: s_write_resp :: 35 | s_send :: s_done :: Nil) = Enum(5) 36 | val state = RegInit(s_start) 37 | 38 | val sendReqVec = VecInit(sendReqs.map { 39 | case (addr, len, part) => Cat(part.B, len.U(lenBits.W), addr.U(addrBits.W))}) 40 | 41 | val sendReqAddrVec = VecInit(sendReqs.map{case (addr, _, _) => addr.U(addrBits.W)}) 42 | val sendReqCounts = sendReqs.map { case (_, len, _) => len / beatBytes } 43 | val sendReqCountVec = VecInit(sendReqCounts.map(_.U)) 44 | val sendReqBase = VecInit((0 +: (1 until sendReqs.size).map( 45 | i => sendReqCounts.slice(0, i).reduce(_ + _))).map(_.U(addrBits.W))) 46 | val maxMemCount = sendReqCounts.reduce(max(_, _)) 47 | val totalMemCount = sendReqCounts.reduce(_ + _) 48 | 49 | require(totalMemCount == sendData.size) 50 | 51 | val sendDataVec = VecInit(sendData.map(_.U(dataBits.W))) 52 | 53 | val reqIdx = Reg(UInt(log2Ceil(sendReqs.size).W)) 54 | val memIdx = Reg(UInt(log2Ceil(totalMemCount).W)) 55 | 56 | val outSend = TwoWayCounter( 57 | io.send.req.fire, io.send.comp.fire, sendReqs.size) 58 | 59 | val writeAddr = sendReqAddrVec(reqIdx) + (memIdx << byteAddrBits.U) 60 | val writeData = sendDataVec(sendReqBase(reqIdx) + memIdx) 61 | 62 | tl.a.valid := state === s_write_req 63 | tl.a.bits := edge.Put( 64 | fromSource = 0.U, 65 | toAddress = writeAddr, 66 | lgSize = byteAddrBits.U, 67 | data = writeData)._2 68 | tl.d.ready := state === s_write_resp 69 | 70 | io.send.req.valid := state === s_send 71 | io.send.req.bits := sendReqVec(reqIdx) 72 | io.send.comp.ready := outSend =/= 0.U 73 | 74 | io.finished := state === s_done && outSend === 0.U 75 | 76 | when (state === s_start && io.start) { 77 | reqIdx := 0.U 78 | memIdx := 0.U 79 | state := s_write_req 80 | } 81 | 82 | when (tl.a.fire) { state := s_write_resp } 83 | 84 | when (tl.d.fire) { 85 | memIdx := memIdx + 1.U 86 | state := s_write_req 87 | 88 | when (memIdx === (sendReqCountVec(reqIdx) - 1.U)) { 89 | memIdx := 0.U 90 | reqIdx := reqIdx + 1.U 91 | when (reqIdx === (sendReqs.size - 1).U) { 92 | reqIdx := 0.U 93 | state := s_send 94 | } 95 | } 96 | } 97 | 98 | when (io.send.req.fire) { 99 | reqIdx := reqIdx + 1.U 100 | when (reqIdx === (sendReqs.size - 1).U) { 101 | reqIdx := 0.U 102 | state := s_done 103 | } 104 | } 105 | } 106 | } 107 | 108 | class IceNicTestRecvDriver(recvReqs: Seq[Int], recvData: Seq[BigInt]) 109 | (implicit p: Parameters) extends LazyModule { 110 | val node = TLClientNode(Seq(TLMasterPortParameters.v1(Seq(TLClientParameters( 111 | name = "test-recv-driver", sourceId = IdRange(0, 1)))))) 112 | 113 | lazy val module = new Impl 114 | class Impl extends LazyModuleImp(this) { 115 | val io = IO(new Bundle with UnitTestIO { 116 | val recv = new IceNicRecvIO 117 | }) 118 | 119 | val (tl, edge) = node.out(0) 120 | val dataBits = tl.params.dataBits 121 | val beatBytes = dataBits / 8 122 | val byteAddrBits = log2Ceil(beatBytes) 123 | 124 | val (s_start :: s_recv :: s_wait :: 125 | s_check_req :: s_check_resp :: s_done :: Nil) = Enum(6) 126 | val state = RegInit(s_start) 127 | 128 | val recvReqVec = VecInit(recvReqs.map(_.U(NET_IF_WIDTH.W))) 129 | val recvDataVec = VecInit(recvData.map(_.U(dataBits.W))) 130 | 131 | val reqIdx = Reg(UInt(log2Ceil(recvReqs.size).W)) 132 | val memIdx = Reg(UInt(log2Ceil(recvData.size).W)) 133 | 134 | val outRecv = TwoWayCounter( 135 | io.recv.req.fire, io.recv.comp.fire, recvReqVec.size) 136 | 137 | tl.a.valid := state === s_check_req 138 | tl.a.bits := edge.Get( 139 | fromSource = 0.U, 140 | toAddress = recvReqVec.head + (memIdx << byteAddrBits.U), 141 | lgSize = byteAddrBits.U)._2 142 | tl.d.ready := state === s_check_resp 143 | 144 | io.recv.req.valid := state === s_recv 145 | io.recv.req.bits := recvReqVec(reqIdx) 146 | io.recv.comp.ready := outRecv =/= 0.U 147 | 148 | io.finished := state === s_done 149 | 150 | when (state === s_start && io.start) { 151 | reqIdx := 0.U 152 | memIdx := 0.U 153 | state := s_recv 154 | } 155 | 156 | when (io.recv.req.fire) { 157 | reqIdx := reqIdx + 1.U 158 | when (reqIdx === (recvReqVec.size - 1).U) { 159 | reqIdx := 0.U 160 | state := s_wait 161 | } 162 | } 163 | 164 | when (state === s_wait && outRecv === 0.U) { 165 | state := s_check_req 166 | } 167 | 168 | when (state === s_check_req && tl.a.ready) { 169 | state := s_check_resp 170 | } 171 | 172 | when (state === s_check_resp && tl.d.valid) { 173 | memIdx := memIdx + 1.U 174 | state := s_check_req 175 | when (memIdx === (recvData.size - 1).U) { 176 | memIdx := 0.U 177 | state := s_done 178 | } 179 | } 180 | 181 | assert(!tl.d.valid || tl.d.bits.data === recvDataVec(memIdx), 182 | "IceNicTest: Received wrong data") 183 | } 184 | } 185 | 186 | class IceNicRecvTest(implicit p: Parameters) extends NICLazyModule { 187 | val recvReqs = Seq(0, 1440, 1464) 188 | // The 90-flit packet should be dropped 189 | val recvLens = Seq(180, 3, 90, 7) 190 | val testData = Seq.tabulate(280) { i => BigInt(i << 4) } 191 | val recvData = testData.take(183) ++ testData.drop(273) 192 | 193 | val nicParams = p.alterPartial({ 194 | case NICKey => Some(p(NICKey).get.copy(inBufFlits = 200)) 195 | }) 196 | val recvDriver = LazyModule(new IceNicTestRecvDriver(recvReqs, recvData)) 197 | val recvPath = LazyModule(new IceNicRecvPath()(nicParams)) 198 | val xbar = LazyModule(new TLXbar) 199 | val mem = LazyModule(new TLRAM( 200 | AddressSet(0, 0x7ff), beatBytes = NET_IF_BYTES)) 201 | 202 | val MEM_LATENCY = 32 203 | val RLIMIT_INC = 1 204 | val RLIMIT_PERIOD = 4 205 | val RLIMIT_SIZE = 8 206 | 207 | xbar.node := recvDriver.node 208 | xbar.node := recvPath.node 209 | mem.node := TLFragmenter(NET_IF_BYTES, maxAcquireBytes) := TLBuffer.chainNode(MEM_LATENCY) := xbar.node 210 | 211 | lazy val module = new Impl 212 | class Impl extends LazyModuleImp(this) { 213 | val io = IO(new Bundle with UnitTestIO) 214 | 215 | val gen = Module(new PacketGen(recvLens, testData)) 216 | gen.io.start := io.start 217 | recvDriver.module.io.start := gen.io.finished 218 | recvPath.module.io.recv <> recvDriver.module.io.recv 219 | recvPath.module.io.in <> RateLimiter( 220 | gen.io.out, RLIMIT_INC, RLIMIT_PERIOD, RLIMIT_SIZE) 221 | io.finished := recvDriver.module.io.finished 222 | } 223 | } 224 | 225 | class IceNicRecvTestWrapper(implicit p: Parameters) extends UnitTest(20000) { 226 | val test = Module(LazyModule(new IceNicRecvTest).module) 227 | test.io.start := io.start 228 | io.finished := test.io.finished 229 | } 230 | 231 | class IceNicSendTest(implicit p: Parameters) extends LazyModule { 232 | val sendReqs = Seq( 233 | (2, 10, true), 234 | (17, 6, false), 235 | (24, 12, false)) 236 | val sendData = Seq( 237 | BigInt("7766554433221100", 16), 238 | BigInt("FFEEDDCCBBAA9988", 16), 239 | BigInt("0123456789ABCDEF", 16), 240 | BigInt("FEDCBA9876543210", 16), 241 | BigInt("76543210FDECBA98", 16)) 242 | 243 | val recvData = Seq( 244 | BigInt("9988776655443322", 16), 245 | BigInt("23456789ABCDBBAA", 16), 246 | BigInt("FEDCBA9876543210", 16), 247 | BigInt("00000000FDECBA98", 16)) 248 | val recvKeep = Seq(0xFF, 0xFF, 0xFF, 0x0F) 249 | val recvLast = Seq(false, true, false, true) 250 | 251 | val sendPath = LazyModule(new IceNicSendPath) 252 | val rom = LazyModule(new TLROM(0, 64, 253 | sendData.flatMap( 254 | data => (0 until 8).map(i => ((data >> (i * 8)) & 0xff).toByte)), 255 | beatBytes = 8)) 256 | 257 | rom.node := TLFragmenter(NET_IF_BYTES, 16) := TLBuffer() := sendPath.node 258 | 259 | val RLIMIT_INC = 1 260 | val RLIMIT_PERIOD = 0 261 | val RLIMIT_SIZE = 8 262 | 263 | lazy val module = new Impl 264 | class Impl extends LazyModuleImp(this) { 265 | val io = IO(new Bundle with UnitTestIO) 266 | 267 | val sendPathIO = sendPath.module.io 268 | val sendReqVec = VecInit(sendReqs.map { case (start, len, part) => 269 | Cat(part.B, len.U(15.W), start.U(48.W)) 270 | }) 271 | val (sendReqIdx, sendReqDone) = Counter(sendPathIO.send.req.fire, sendReqs.size) 272 | val (sendCompIdx, sendCompDone) = Counter(sendPathIO.send.comp.fire, sendReqs.size) 273 | 274 | val started = RegInit(false.B) 275 | val requesting = RegInit(false.B) 276 | val completing = RegInit(false.B) 277 | 278 | sendPathIO.send.req.valid := requesting 279 | sendPathIO.send.req.bits := sendReqVec(sendReqIdx) 280 | sendPathIO.send.comp.ready := completing 281 | 282 | when (!started && io.start) { 283 | requesting := true.B 284 | completing := true.B 285 | } 286 | when (sendReqDone) { requesting := false.B } 287 | when (sendCompDone) { completing := false.B } 288 | 289 | sendPathIO.rlimit.inc := RLIMIT_INC.U 290 | sendPathIO.rlimit.period := RLIMIT_PERIOD.U 291 | sendPathIO.rlimit.size := RLIMIT_SIZE.U 292 | 293 | val check = Module(new PacketCheck(recvData, recvKeep, recvLast)) 294 | check.io.in <> sendPathIO.out 295 | io.finished := check.io.finished && !completing && !requesting 296 | } 297 | } 298 | 299 | class IceNicSendTestWrapper(implicit p: Parameters) extends UnitTest { 300 | val test = Module(LazyModule(new IceNicSendTest).module) 301 | test.io.start := io.start 302 | io.finished := test.io.finished 303 | } 304 | 305 | class IceNicTest(implicit p: Parameters) extends NICLazyModule { 306 | val sendReqs = Seq( 307 | (0, 128, true), 308 | (144, 160, false), 309 | (320, 64, false)) 310 | val recvReqs = Seq(256, 544) 311 | val testData = Seq.tabulate(44)(i => BigInt(i << 4)) 312 | 313 | val sendDriver = LazyModule(new IceNicTestSendDriver(sendReqs, testData)) 314 | val recvDriver = LazyModule(new IceNicTestRecvDriver(recvReqs, testData)) 315 | val sendPath = LazyModule(new IceNicSendPath) 316 | val recvPath = LazyModule(new IceNicRecvPath) 317 | val xbar = LazyModule(new TLXbar) 318 | val mem = LazyModule(new TLRAM( 319 | AddressSet(0, 0x1ff), beatBytes = NET_IF_BYTES)) 320 | 321 | val NET_LATENCY = 64 322 | val MEM_LATENCY = 32 323 | val RLIMIT_INC = 1 324 | val RLIMIT_PERIOD = 0 325 | val RLIMIT_SIZE = 8 326 | 327 | xbar.node := sendDriver.node 328 | xbar.node := recvDriver.node 329 | xbar.node := sendPath.node 330 | xbar.node := recvPath.node 331 | mem.node := TLFragmenter(NET_IF_BYTES, maxAcquireBytes) := TLBuffer.chainNode(MEM_LATENCY) := xbar.node 332 | 333 | lazy val module = new Impl 334 | class Impl extends LazyModuleImp(this) { 335 | val io = IO(new Bundle with UnitTestIO) 336 | 337 | sendPath.module.io.send <> sendDriver.module.io.send 338 | recvPath.module.io.recv <> recvDriver.module.io.recv 339 | 340 | sendPath.module.io.rlimit.inc := RLIMIT_INC.U 341 | sendPath.module.io.rlimit.period := RLIMIT_PERIOD.U 342 | sendPath.module.io.rlimit.size := RLIMIT_SIZE.U 343 | 344 | recvPath.module.io.in <> LatencyPipe(sendPath.module.io.out, NET_LATENCY) 345 | 346 | sendDriver.module.io.start := io.start 347 | recvDriver.module.io.start := io.start 348 | io.finished := sendDriver.module.io.finished && recvDriver.module.io.finished 349 | 350 | val count_start :: count_up :: count_print :: count_done :: Nil = Enum(4) 351 | val count_state = RegInit(count_start) 352 | val cycle_count = Reg(UInt(64.W)) 353 | val recv_count = Reg(UInt(1.W)) 354 | 355 | when (count_state === count_start && sendPath.module.io.send.req.fire) { 356 | count_state := count_up 357 | cycle_count := 0.U 358 | recv_count := 1.U 359 | } 360 | when (count_state === count_up) { 361 | cycle_count := cycle_count + 1.U 362 | when (recvPath.module.io.recv.comp.fire) { 363 | recv_count := recv_count - 1.U 364 | when (recv_count === 0.U) { count_state := count_print } 365 | } 366 | } 367 | when (count_state === count_print) { 368 | printf("NIC test completed in %d cycles\n", cycle_count) 369 | count_state := count_done 370 | } 371 | } 372 | } 373 | 374 | class IceNicTestWrapper(implicit p: Parameters) extends UnitTest(50000) { 375 | val test = Module(LazyModule(new IceNicTest).module) 376 | test.io.start := io.start 377 | io.finished := test.io.finished 378 | } 379 | 380 | class MisalignedTestDriver(implicit p: Parameters) extends Module { 381 | val io = IO(new Bundle with UnitTestIO { 382 | val net = new StreamIO(NET_IF_WIDTH) 383 | val send = new IceNicSendIO 384 | val recv = new IceNicRecvIO 385 | }) 386 | 387 | val fullData = Seq( 388 | "hDEADBEEF", "h01234567", "h555999ff", "h09341423", 389 | "h1384AEDF", "hABCD1234", "h1093567A", "hBADD00D1", 390 | "hA325B246", "h49230923", "h11113333", "hAEDC1445").map(_.U(32.W)) 391 | 392 | val outData1 = VecInit( 393 | Cat(fullData(8), fullData(7)), 394 | Cat(fullData(10), fullData(9)), 395 | Cat(0.U(32.W), fullData(11))) 396 | val outKeep1 = VecInit(Seq.fill(2)(~0.U(8.W)) :+ "h0f".U) 397 | 398 | val outData2 = VecInit( 399 | Cat(fullData(1), fullData(0)), 400 | Cat(fullData(3), fullData(2)), 401 | Cat(fullData(5), fullData(4)), 402 | Cat(0.U(32.W), fullData(6))) 403 | val outKeep2 = VecInit(Seq.fill(3)(~0.U(8.W)) :+ "h0f".U) 404 | 405 | val expData = VecInit( 406 | Cat(fullData(6), fullData(5)), 407 | Cat(fullData(8), fullData(7)), 408 | Cat(fullData(10), fullData(9))) 409 | 410 | val (s_start :: s_sendreq :: s_sendcomp :: s_recvreq :: s_recvcomp :: 411 | s_outdata1 :: s_outdata2 :: s_indata :: s_done :: Nil) = Enum(9) 412 | val state = RegInit(s_start) 413 | 414 | val (recvReqIdx, recvReqDone) = Counter(io.recv.req.fire, 2) 415 | val (recvCompIdx, recvCompDone) = Counter(io.recv.comp.fire, 2) 416 | 417 | val (outIdx1, outDone1) = Counter( 418 | state === s_outdata1 && io.net.out.ready, outData1.size) 419 | val (outIdx2, outDone2) = Counter( 420 | state === s_outdata2 && io.net.out.ready, outData2.size) 421 | val (inIdx, inDone) = Counter(io.net.in.fire, expData.size) 422 | 423 | val outBits1 = Wire(new StreamChannel(NET_IF_WIDTH)) 424 | outBits1.data := outData1(outIdx1) 425 | outBits1.keep := outKeep1(outIdx1) 426 | outBits1.last := outIdx1 === (outData1.size - 1).U 427 | 428 | val outBits2 = Wire(new StreamChannel(NET_IF_WIDTH)) 429 | outBits2.data := outData2(outIdx2) 430 | outBits2.keep := outKeep2(outIdx2) 431 | outBits2.last := outIdx2 === (outData2.size - 1).U 432 | 433 | io.send.req.valid := state === s_sendreq 434 | io.send.req.bits := Cat((expData.size * 8).U(16.W), 20.U(48.W)) 435 | io.send.comp.ready := state === s_sendcomp 436 | 437 | io.recv.req.valid := state === s_recvreq 438 | io.recv.req.bits := Mux(recvReqIdx === 0.U, 28.U, 0.U) 439 | io.recv.comp.ready := state === s_recvcomp 440 | 441 | io.net.out.valid := state.isOneOf(s_outdata1, s_outdata2) 442 | io.net.out.bits := Mux(state === s_outdata1, outBits1, outBits2) 443 | io.net.in.ready := state === s_indata 444 | 445 | io.finished := state === s_done 446 | 447 | when (state === s_start && io.start) { state := s_recvreq } 448 | 449 | when (recvReqDone) { state := s_outdata1 } 450 | 451 | when (outDone1) { state := s_outdata2 } 452 | when (outDone2) { state := s_recvcomp } 453 | 454 | when (recvCompDone) { state := s_sendreq } 455 | 456 | when (io.send.req.fire) { state := s_indata } 457 | 458 | when (inDone) { state := s_done } 459 | 460 | assert(!io.net.in.valid || io.net.in.bits.data === expData(inIdx), 461 | "MisalignedTest: input data does not match expected") 462 | assert(!io.net.in.valid || io.net.in.bits.keep.andR, 463 | "MisalignedTest: input keep does not match expected") 464 | assert(!io.net.in.valid || io.net.in.bits.last === (inIdx === (expData.size-1).U), 465 | "MisalignedTest: input last does not match expected") 466 | 467 | } 468 | 469 | class MisalignedTest(implicit p: Parameters) extends NICLazyModule { 470 | val sendpath = LazyModule(new IceNicSendPath) 471 | val recvpath = LazyModule(new IceNicRecvPath) 472 | 473 | val xbar = LazyModule(new TLXbar) 474 | val mem = LazyModule(new TLRAM( 475 | AddressSet(0, 0x7ff), beatBytes = NET_IF_BYTES)) 476 | 477 | xbar.node := sendpath.node 478 | xbar.node := recvpath.node 479 | mem.node := TLFragmenter(NET_IF_BYTES, maxAcquireBytes) := 480 | TLBuffer() := xbar.node 481 | 482 | lazy val module = new Impl 483 | class Impl extends LazyModuleImp(this) { 484 | val io = IO(new Bundle with UnitTestIO) 485 | 486 | val driver = Module(new MisalignedTestDriver) 487 | 488 | driver.io.start := io.start 489 | io.finished := driver.io.finished 490 | 491 | driver.io.net.in <> sendpath.module.io.out 492 | recvpath.module.io.in <> driver.io.net.out 493 | 494 | sendpath.module.io.send.req <> Queue(driver.io.send.req, 1) 495 | recvpath.module.io.recv.req <> Queue(driver.io.recv.req, 2) 496 | driver.io.send.comp <> Queue(sendpath.module.io.send.comp, 1) 497 | driver.io.recv.comp <> Queue(recvpath.module.io.recv.comp, 2) 498 | 499 | val rlimit = sendpath.module.io.rlimit 500 | rlimit.inc := 1.U 501 | rlimit.period := 0.U 502 | rlimit.size := 8.U 503 | } 504 | } 505 | 506 | class MisalignedTestWrapper(implicit p: Parameters) extends UnitTest { 507 | val test = Module(LazyModule(new MisalignedTest).module) 508 | test.io.start := io.start 509 | io.finished := test.io.finished 510 | } 511 | -------------------------------------------------------------------------------- /src/main/scala/Pauser.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.util.{HellaPeekingArbiter} 6 | import freechips.rocketchip.unittest.UnitTest 7 | import IceNetConsts._ 8 | 9 | object PauseConsts { 10 | val MAC_ETHTYPE = 0x8808 11 | val PAUSE_CTRL = 0x0001 12 | val BT_PER_QUANTA = 512 13 | val CYCLES_PER_QUANTA = BT_PER_QUANTA / NET_IF_WIDTH 14 | val MULTICAST_MACADDR = 0x010000C28001L 15 | } 16 | import PauseConsts._ 17 | 18 | trait NetworkEndianHelpers { 19 | def ntohs(x: UInt) = Cat(x(7, 0), x(15, 8)) 20 | def htons(x: UInt) = ntohs(x) 21 | def ntohl(x: UInt) = Cat(ntohs(x(15, 0)), ntohs(x(31, 16))) 22 | def htonl(x: UInt) = ntohl(x) 23 | } 24 | 25 | class PauserSettings extends Bundle { 26 | val threshold = UInt(16.W) 27 | val quanta = UInt(16.W) 28 | val refresh = UInt(16.W) 29 | } 30 | 31 | object PauseDropCheck extends NetworkEndianHelpers { 32 | def apply(header: EthernetHeader, ch: StreamChannel, update: Bool): Bool = { 33 | val first = RegInit(true.B) 34 | val isPause = ntohs(header.ethType) === MAC_ETHTYPE.U && 35 | ntohs(ch.data(15, 0)) === PAUSE_CTRL.U && first 36 | val isPauseReg = RegInit(false.B) 37 | 38 | when (update && first) { first := false.B; isPauseReg := isPause } 39 | when (update && ch.last) { first := true.B; isPauseReg := false.B } 40 | 41 | isPause || isPauseReg 42 | } 43 | } 44 | 45 | /** 46 | * Flow control unit using Ethernet pause frames 47 | * See https://en.wikipedia.org/wiki/Ethernet_flow_control#Pause_frame 48 | * @creditInit Size of each buffer being tracked 49 | * @nBuckets Number of buffers being tracked 50 | */ 51 | class Pauser(creditInit: Int, nBuckets: Int) extends Module 52 | with NetworkEndianHelpers { 53 | val timerBits = 16 + log2Ceil(CYCLES_PER_QUANTA) 54 | val creditBits = log2Ceil(creditInit + 1) 55 | 56 | val io = IO(new Bundle { 57 | val ext = new StreamIO(NET_IF_WIDTH) 58 | val int = Flipped(new StreamIO(NET_IF_WIDTH)) 59 | val in_free = Input(Vec(nBuckets, UInt(8.W))) 60 | val macAddr = Input(UInt(48.W)) 61 | val settings = Input(new PauserSettings) 62 | }) 63 | 64 | val credits = RegInit(VecInit(Seq.fill(nBuckets)(creditInit.U(16.W)))) 65 | val outPauseTimer = RegInit(0.U(timerBits.W)) 66 | val outPaused = outPauseTimer > 0.U 67 | 68 | io.int.in <> io.ext.in 69 | 70 | val headerBeats = ETH_HEAD_BYTES / NET_IF_BYTES 71 | val inHeaderVec = Reg(Vec(headerBeats, UInt(NET_IF_WIDTH.W))) 72 | val inHeader = inHeaderVec.asTypeOf(new EthernetHeader) 73 | 74 | val s_head :: s_mac :: s_tail :: Nil = Enum(3) 75 | val state = RegInit(s_head) 76 | 77 | val inHeadIdx = RegInit(0.U(log2Ceil(headerBeats).W)) 78 | 79 | when (outPaused) { outPauseTimer := outPauseTimer - 1.U } 80 | 81 | when (io.int.in.fire) { 82 | val data = io.int.in.bits.data 83 | 84 | switch (state) { 85 | is (s_head) { 86 | inHeaderVec(inHeadIdx) := data 87 | inHeadIdx := inHeadIdx + 1.U 88 | when (inHeadIdx === (headerBeats-1).U) { state := s_mac } 89 | } 90 | is (s_mac) { 91 | val isMac = ntohs(inHeader.ethType) === MAC_ETHTYPE.U 92 | val isPause = ntohs(data(15, 0)) === PAUSE_CTRL.U 93 | val quanta = ntohs(data(31, 16)) 94 | val cycles = quanta << log2Ceil(CYCLES_PER_QUANTA).U 95 | when (isMac && isPause) { outPauseTimer := cycles } 96 | state := s_tail 97 | } 98 | } 99 | 100 | when (io.int.in.bits.last) { 101 | state := s_head 102 | inHeadIdx := 0.U 103 | } 104 | } 105 | 106 | for (i <- 0 until nBuckets) { 107 | credits(i) := credits(i) - io.int.in.fire + io.in_free(i) 108 | } 109 | 110 | val arb = Module(new PacketArbiter(2)) 111 | 112 | val outHeader = EthernetHeader( 113 | MULTICAST_MACADDR.U, 114 | io.macAddr, 115 | htons(MAC_ETHTYPE.U(16.W))) 116 | val outVec = VecInit(outHeader.toWords() :+ Cat( 117 | htons(io.settings.quanta), htons(PAUSE_CTRL.U(16.W)))) 118 | val sendPause = RegInit(false.B) 119 | val (outIdx, outDone) = Counter(arb.io.in(0).fire, outVec.size) 120 | 121 | arb.io.in(0).valid := sendPause 122 | arb.io.in(0).bits.data := outVec(outIdx) 123 | arb.io.in(0).bits.keep := NET_FULL_KEEP 124 | arb.io.in(0).bits.last := outIdx === (outVec.size-1).U 125 | 126 | val inPauseTimer = RegInit(0.U(16.W)) 127 | val inPaused = inPauseTimer > 0.U 128 | 129 | val creditsLow = credits.map(_ < io.settings.threshold).reduce(_ || _) 130 | 131 | when (outDone) { 132 | inPauseTimer := io.settings.refresh 133 | sendPause := false.B 134 | } 135 | when (inPaused) { inPauseTimer := inPauseTimer - 1.U } 136 | when (creditsLow && !sendPause && !inPaused) { 137 | sendPause := true.B 138 | } 139 | 140 | val outInProgress = RegInit(false.B) 141 | val canForward = !outPaused || outInProgress 142 | 143 | arb.io.in(1).valid := canForward && io.int.out.valid 144 | arb.io.in(1).bits := io.int.out.bits 145 | io.int.out.ready := canForward && arb.io.in(1).ready 146 | 147 | when (arb.io.in(1).fire) { 148 | when (!outInProgress) { outInProgress := true.B } 149 | when (arb.io.in(1).bits.last) { outInProgress := false.B } 150 | } 151 | 152 | io.ext.out <> arb.io.out 153 | } 154 | 155 | class PauserComplex(nFlits: Int) extends Module { 156 | val creditBits = log2Ceil(nFlits + 1) 157 | val io = IO(new Bundle { 158 | val ext = new StreamIO(NET_IF_WIDTH) 159 | val int = Flipped(new StreamIO(NET_IF_WIDTH)) 160 | val macAddr = Input(UInt(48.W)) 161 | val settings = Input(new PauserSettings) 162 | }) 163 | 164 | val pauser = Module(new Pauser(nFlits, 1)) 165 | val buffer = Module(new NetworkPacketBuffer( 166 | nFlits, dropChecks = Seq(PauseDropCheck(_, _, _)), dropless = true)) 167 | 168 | io.ext <> pauser.io.ext 169 | pauser.io.macAddr := io.macAddr 170 | pauser.io.settings := io.settings 171 | 172 | pauser.io.int.out <> io.int.out 173 | buffer.io.stream.in <> pauser.io.int.in 174 | pauser.io.in_free(0) := buffer.io.free 175 | io.int.in <> buffer.io.stream.out 176 | } 177 | 178 | class PauserTest extends UnitTest { 179 | val nFlits = 400 180 | val latency = 128 181 | val packetWords = ETH_STANDARD_MAX_BYTES / NET_IF_BYTES 182 | val threshold = latency + packetWords 183 | val pauseQuanta = threshold / CYCLES_PER_QUANTA 184 | val pauseRefresh = packetWords 185 | 186 | val lcomplex = Module(new PauserComplex(nFlits)) 187 | val rcomplex = Module(new PauserComplex(nFlits)) 188 | 189 | rcomplex.io.ext.flipConnect(NetDelay(lcomplex.io.ext, latency)) 190 | rcomplex.io.int.out <> rcomplex.io.int.in 191 | 192 | val lmacaddr = (0x2L << 40).U(48.W) 193 | val rmacaddr = (0x3L << 40).U(48.W) 194 | 195 | lcomplex.io.macAddr := lmacaddr 196 | rcomplex.io.macAddr := rmacaddr 197 | lcomplex.io.settings.threshold := threshold.U 198 | rcomplex.io.settings.threshold := threshold.U 199 | lcomplex.io.settings.quanta := pauseQuanta.U 200 | rcomplex.io.settings.quanta := pauseQuanta.U 201 | lcomplex.io.settings.refresh := pauseRefresh.U 202 | rcomplex.io.settings.refresh := pauseRefresh.U 203 | 204 | val ethHeader = EthernetHeader(rmacaddr, lmacaddr, 0x0000.U) 205 | val pktData = VecInit( 206 | ethHeader.toWords() ++ Seq.tabulate(130)(_.U(NET_IF_WIDTH.W))) 207 | val nPackets = 8 208 | 209 | val started = RegInit(false.B) 210 | 211 | val sending = RegInit(false.B) 212 | val (sendIdx, sendPktDone) = Counter(lcomplex.io.int.out.fire, pktData.size) 213 | val (sendPhase, sendDone) = Counter(sendPktDone, nPackets) 214 | 215 | val receiving = RegInit(false.B) 216 | val (recvIdx, recvPktDone) = Counter(lcomplex.io.int.in.fire, pktData.size) 217 | val (recvPhase, recvDone) = Counter(recvPktDone, nPackets) 218 | 219 | lcomplex.io.int.out.valid := sending 220 | lcomplex.io.int.out.bits.data := pktData(sendIdx) 221 | lcomplex.io.int.out.bits.keep := NET_FULL_KEEP 222 | lcomplex.io.int.out.bits.last := sendIdx === (pktData.size-1).U 223 | lcomplex.io.int.in.ready := receiving 224 | 225 | when (!started && io.start) { 226 | started := true.B 227 | sending := true.B 228 | receiving := true.B 229 | } 230 | when (sendDone) { sending := false.B } 231 | when (recvDone) { receiving := false.B } 232 | 233 | val recv = lcomplex.io.int.in 234 | assert(!recv.valid || 235 | (recv.bits.data === pktData(recvIdx) && 236 | recv.bits.keep === NET_FULL_KEEP && 237 | recv.bits.last === (recvIdx === (pktData.size-1).U)), 238 | "PauserTest: received incorrect data, keep, or last") 239 | 240 | io.finished := started && !sending && !receiving 241 | } 242 | -------------------------------------------------------------------------------- /src/main/scala/Stream.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | 6 | // These classes originated in testchipip, but are copied 7 | // here to reduce dependencies 8 | class StreamChannel(val w: Int) extends Bundle { 9 | val data = UInt(w.W) 10 | val keep = UInt((w/8).W) 11 | val last = Bool() 12 | } 13 | 14 | class StreamIO(val w: Int) extends Bundle { 15 | val in = Flipped(Decoupled(new StreamChannel(w))) 16 | val out = Decoupled(new StreamChannel(w)) 17 | 18 | def flipConnect(other: StreamIO) { 19 | in <> other.out 20 | other.in <> out 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/scala/TCAM.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import org.chipsalliance.cde.config.Parameters 6 | import freechips.rocketchip.diplomacy._ 7 | import freechips.rocketchip.tilelink._ 8 | 9 | class TCAMMatchIO(n: Int, dataBits: Int) extends Bundle { 10 | val addrBits = log2Ceil(n) 11 | val data = Output(UInt(dataBits.W)) 12 | val addr = Input(UInt(addrBits.W)) 13 | val found = Input(Bool()) 14 | 15 | } 16 | 17 | class TCAM(address: BigInt, val n: Int, val dataBits: Int, val nPorts: Int) 18 | (implicit p: Parameters) extends LazyModule { 19 | val addrBits = log2Ceil(n) 20 | val byteAddrBits = log2Ceil(dataBits / 8) 21 | val beatBytes = 1 << byteAddrBits 22 | val addrMask = (1 << (1 + addrBits + byteAddrBits)) - 1 23 | 24 | val node = TLManagerNode(Seq(TLSlavePortParameters.v1(Seq(TLSlaveParameters.v1( 25 | address = Seq(AddressSet(address, addrMask)), 26 | regionType = RegionType.PUT_EFFECTS, 27 | supportsGet = TransferSizes(1, beatBytes), 28 | supportsPutFull = TransferSizes(1, beatBytes))), beatBytes))) 29 | 30 | lazy val module = new TCAMModule(this) 31 | } 32 | 33 | class TCAMModule(outer: TCAM) extends LazyModuleImp(outer) { 34 | val io = IO(new Bundle { 35 | val tcam = Flipped( 36 | Vec(outer.nPorts, new TCAMMatchIO(outer.n, outer.dataBits))) 37 | }) 38 | 39 | val (tl, edge) = outer.node.in(0) 40 | 41 | val dataArr = Reg(Vec(outer.n, UInt(outer.dataBits.W))) 42 | val maskArr = Reg(Vec(outer.n, UInt(outer.dataBits.W))) 43 | 44 | val acq = Queue(tl.a, 1) 45 | val regsel = acq.bits.address(outer.addrBits + outer.byteAddrBits) 46 | val wordaddr = acq.bits.address( 47 | outer.addrBits + outer.byteAddrBits - 1, outer.byteAddrBits) 48 | 49 | tl.d.valid := acq.valid 50 | acq.ready := tl.d.ready 51 | tl.d.bits := edge.AccessAck(acq.bits, 0.U) 52 | tl.d.bits.opcode := Mux(edge.hasData(acq.bits), 53 | TLMessages.AccessAck, TLMessages.AccessAckData) 54 | tl.d.bits.data := Mux(regsel, maskArr(wordaddr), dataArr(wordaddr)) 55 | 56 | when (acq.fire && edge.hasData(acq.bits)) { 57 | when (regsel) { 58 | maskArr(wordaddr) := acq.bits.data(outer.dataBits - 1, 0) 59 | } .otherwise { 60 | dataArr(wordaddr) := acq.bits.data(outer.dataBits - 1, 0) 61 | } 62 | } 63 | 64 | io.tcam.foreach { m => 65 | val matches = dataArr.zip(maskArr).map { case (data, mask) => 66 | !((m.data ^ data) & mask).orR 67 | } 68 | m.addr := PriorityEncoder(matches) 69 | m.found := matches.reduce(_ || _) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/scala/Tap.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.unittest.UnitTest 6 | import freechips.rocketchip.util.UIntIsOneOf 7 | import scala.math.max 8 | import IceNetConsts._ 9 | 10 | class NetworkTap[T <: Data]( 11 | selectFuncs: Seq[T => Bool], 12 | headerTyp: T = new EthernetHeader, 13 | headerBytes: Int = ETH_HEAD_BYTES, 14 | wordBytes: Int = NET_IF_WIDTH / 8) extends Module { 15 | 16 | val wordBits = wordBytes * 8 17 | val headerWords = headerBytes / wordBytes 18 | val n = selectFuncs.length 19 | 20 | val io = IO(new Bundle { 21 | val inflow = Flipped(Decoupled(new StreamChannel(wordBits))) 22 | val passthru = Decoupled(new StreamChannel(wordBits)) 23 | val tapout = Vec(n, Decoupled(new StreamChannel(wordBits))) 24 | }) 25 | 26 | assert(n > 0, "NetworkTap must have at least one output tap") 27 | 28 | val headerVec = Reg(Vec(headerWords, UInt(wordBits.W))) 29 | val header = headerVec.asTypeOf(headerTyp) 30 | 31 | val idxBits = log2Ceil(headerWords) 32 | val headerIdx = RegInit(0.U(idxBits.W)) 33 | val headerLen = Reg(UInt(idxBits.W)) 34 | val bodyLess = Reg(Bool()) 35 | 36 | val (s_collect_header :: s_check_header :: 37 | s_output_header :: s_forward_body :: Nil) = Enum(4) 38 | val state = RegInit(s_collect_header) 39 | val route = Reg(Vec(n, Bool())) 40 | 41 | val selectedReady = MuxCase(io.passthru.ready, 42 | route.zip(io.tapout.map(_.ready))) 43 | 44 | io.inflow.ready := MuxLookup(state, false.B)(Seq( 45 | s_collect_header -> true.B, 46 | s_forward_body -> selectedReady)) 47 | 48 | val hasTapout = route.reduce(_ || _) 49 | 50 | io.passthru.valid := MuxLookup(Cat(state, hasTapout), false.B)(Seq( 51 | Cat(s_output_header, false.B) -> true.B, 52 | Cat(s_forward_body, false.B) -> io.inflow.valid)) 53 | io.passthru.bits.data := MuxLookup(Cat(state, hasTapout), 0.U)(Seq( 54 | Cat(s_output_header, false.B) -> headerVec(headerIdx), 55 | Cat(s_forward_body, false.B) -> io.inflow.bits.data)) 56 | io.passthru.bits.last := MuxLookup(Cat(state, hasTapout), false.B)(Seq( 57 | Cat(s_output_header, false.B) -> (bodyLess && headerIdx === headerLen), 58 | Cat(s_forward_body, false.B) -> io.inflow.bits.last)) 59 | io.passthru.bits.keep := NET_FULL_KEEP 60 | 61 | io.tapout.zip(route).foreach { case (tapout, sel) => 62 | tapout.valid := MuxLookup(Cat(state, sel), false.B)(Seq( 63 | Cat(s_output_header, true.B) -> true.B, 64 | Cat(s_forward_body, true.B) -> io.inflow.valid)) 65 | tapout.bits.data := MuxLookup(Cat(state, sel), 0.U)(Seq( 66 | Cat(s_output_header, true.B) -> headerVec(headerIdx), 67 | Cat(s_forward_body, true.B) -> io.inflow.bits.data)) 68 | tapout.bits.last := MuxLookup(Cat(state, sel), false.B)(Seq( 69 | Cat(s_output_header, true.B) -> (bodyLess && headerIdx === headerLen), 70 | Cat(s_forward_body, true.B) -> io.inflow.bits.last)) 71 | tapout.bits.keep := NET_FULL_KEEP 72 | } 73 | 74 | when (state === s_collect_header && io.inflow.valid) { 75 | headerIdx := headerIdx + 1.U 76 | headerVec(headerIdx) := io.inflow.bits.data 77 | 78 | val headerLast = headerIdx === (headerWords-1).U 79 | 80 | when (io.inflow.bits.last || headerLast) { 81 | headerLen := headerIdx 82 | headerIdx := 0.U 83 | bodyLess := io.inflow.bits.last 84 | 85 | when (headerLast) { 86 | state := s_check_header 87 | } .otherwise { 88 | route.foreach(_ := false.B) 89 | state := s_output_header 90 | } 91 | } 92 | } 93 | 94 | when (state === s_check_header) { 95 | route := selectFuncs.map(_(header)) 96 | state := s_output_header 97 | } 98 | 99 | val headerFire = state === s_output_header && selectedReady 100 | val bodyFire = state === s_forward_body && io.inflow.fire 101 | 102 | when (headerFire) { 103 | headerIdx := headerIdx + 1.U 104 | when (headerIdx === headerLen) { 105 | headerIdx := 0.U 106 | state := Mux(bodyLess, s_collect_header, s_forward_body) 107 | } 108 | } 109 | 110 | when (bodyFire && io.inflow.bits.last) { state := s_collect_header } 111 | } 112 | 113 | class NetworkTapTest extends UnitTest { 114 | val sendPayloads = Seq( 115 | Seq(0L, 0x800L << 48, 23L, 13L, 56L, 12L), 116 | Seq(0L, 0x800L << 48), 117 | Seq(0L, 0x801L << 48, 22, 16), 118 | Seq(0L)) 119 | val sendLengths = sendPayloads.map(_.size) 120 | val sendData = sendPayloads.flatten.map(l => BigInt(l)) 121 | 122 | val genIn = Module(new PacketGen(sendLengths, sendData)) 123 | genIn.io.start := io.start 124 | 125 | val tapPayloads = sendPayloads.take(2) 126 | val tapData = tapPayloads.flatten.map(l => BigInt(l)) 127 | val tapKeep = Seq.fill(tapData.size) { 0xff } 128 | val tapLast = tapPayloads.flatMap { 129 | pl => (Seq.fill(pl.size-1) { false } ++ Seq(true)) 130 | } 131 | val checkTap = Module(new PacketCheck(tapData, tapKeep, tapLast)) 132 | 133 | val passPayloads = sendPayloads.drop(2) 134 | val passData = passPayloads.flatten.map(l => BigInt(l)) 135 | val passKeep = Seq.fill(passData.size) { 0xff } 136 | val passLast = passPayloads.flatMap { 137 | pl => (Seq.fill(pl.size-1) { false } ++ Seq(true)) 138 | } 139 | val checkPass = Module(new PacketCheck(passData, passKeep, passLast)) 140 | 141 | val tap = Module(new NetworkTap( 142 | Seq((header: EthernetHeader) => header.ethType === 0x800.U))) 143 | tap.io.inflow <> genIn.io.out 144 | checkTap.io.in <> tap.io.tapout(0) 145 | checkPass.io.in <> tap.io.passthru 146 | 147 | io.finished := checkTap.io.finished || checkPass.io.finished 148 | } 149 | -------------------------------------------------------------------------------- /src/main/scala/TestUtils.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import freechips.rocketchip.util.LatencyPipe 6 | import scala.math.max 7 | import IceNetConsts._ 8 | 9 | class PacketGen(lengths: Seq[Int], genData: Seq[BigInt]) extends Module { 10 | val io = IO(new Bundle { 11 | val start = Input(Bool()) 12 | val finished = Output(Bool()) 13 | val out = Decoupled(new StreamChannel(NET_IF_WIDTH)) 14 | }) 15 | 16 | val maxLength = lengths.reduce(max(_, _)) 17 | val totalLength = lengths.reduce(_ + _) 18 | val lengthVec = VecInit(lengths.map(_.U)) 19 | val dataVec = VecInit(genData.map(_.U(NET_IF_WIDTH.W))) 20 | 21 | require(totalLength == genData.size) 22 | 23 | val pktIdx = Reg(UInt(log2Ceil(lengths.size).W)) 24 | val pktOffset = Reg(UInt(log2Ceil(maxLength).W)) 25 | val dataIdx = Reg(UInt(log2Ceil(totalLength).W)) 26 | val sending = RegInit(false.B) 27 | val started = RegInit(false.B) 28 | 29 | when (!sending && io.start) { 30 | started := true.B 31 | sending := true.B 32 | pktIdx := 0.U 33 | pktOffset := 0.U 34 | dataIdx := 0.U 35 | } 36 | 37 | when (io.out.fire) { 38 | dataIdx := dataIdx + 1.U 39 | pktOffset := pktOffset + 1.U 40 | when (io.out.bits.last) { 41 | pktIdx := pktIdx + 1.U 42 | pktOffset := 0.U 43 | when (pktIdx === (lengths.size - 1).U) { 44 | sending := false.B 45 | } 46 | } 47 | } 48 | 49 | io.out.valid := sending 50 | io.out.bits.data := dataVec(dataIdx) 51 | io.out.bits.keep := NET_FULL_KEEP 52 | io.out.bits.last := pktOffset === lengthVec(pktIdx) - 1.U 53 | io.finished := started && !sending 54 | } 55 | 56 | class PacketCheck( 57 | checkData: Seq[BigInt], 58 | checkKeep: Seq[Int], 59 | checkLast: Seq[Boolean]) extends Module { 60 | val io = IO(new Bundle { 61 | val in = Flipped(Decoupled(new StreamChannel(NET_IF_WIDTH))) 62 | val finished = Output(Bool()) 63 | }) 64 | 65 | val checkDataVec = VecInit(checkData.map(_.U(NET_IF_WIDTH.W))) 66 | val checkKeepVec = VecInit(checkKeep.map(_.U(NET_IF_BYTES.W))) 67 | val checkLastVec = VecInit(checkLast.map(_.B)) 68 | 69 | val (checkIdx, checkDone) = Counter(io.in.fire, checkDataVec.length) 70 | 71 | val finished = RegInit(false.B) 72 | 73 | io.in.ready := !finished 74 | io.finished := finished 75 | 76 | when (checkDone) { finished := true.B } 77 | 78 | def compareData(a: UInt, b: UInt, keep: UInt) = { 79 | val bitmask = FillInterleaved(8, keep) 80 | (a & bitmask) === (b & bitmask) 81 | } 82 | 83 | assert(!io.in.valid || 84 | (compareData(io.in.bits.data, checkDataVec(checkIdx), io.in.bits.keep) && 85 | io.in.bits.keep === checkKeepVec(checkIdx) && 86 | io.in.bits.last === checkLastVec(checkIdx)), 87 | "PacketCheck: input does not match") 88 | 89 | } 90 | 91 | class NetDelay(latency: Int) extends Module { 92 | val io = IO(new Bundle { 93 | val left = new StreamIO(NET_IF_WIDTH) 94 | val right = Flipped(new StreamIO(NET_IF_WIDTH)) 95 | }) 96 | 97 | io.left.out <> LatencyPipe(io.right.out, latency) 98 | io.right.in <> LatencyPipe(io.left.in, latency) 99 | } 100 | 101 | object NetDelay { 102 | def apply(right: StreamIO, latency: Int): StreamIO = { 103 | val delay = Module(new NetDelay(latency)) 104 | delay.io.right.out <> right.out 105 | right.in <> delay.io.right.in 106 | delay.io.left 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/scala/TraceROM.scala: -------------------------------------------------------------------------------- 1 | package icenet 2 | 3 | import chisel3._ 4 | import chisel3.util._ 5 | import IceNetConsts.NET_IF_WIDTH 6 | 7 | class TraceROM extends BlackBox with HasBlackBoxResource{ 8 | val io = IO(new Bundle { 9 | val clock = Input(Clock()) 10 | val reset = Input(Bool()) 11 | val stream = Decoupled(new StreamChannel(NET_IF_WIDTH)) 12 | val macAddr = Output(UInt(48.W)) 13 | val length = Output(UInt(32.W)) 14 | }) 15 | 16 | addResource("/vsrc/TraceROM.v") 17 | addResource("/csrc/TraceROM.cc") 18 | } 19 | --------------------------------------------------------------------------------