diff --git a/sim_udp.md b/sim_udp.md new file mode 100644 index 0000000..45d727c --- /dev/null +++ b/sim_udp.md @@ -0,0 +1,104 @@ + +# Simulated User Datagram Protocol (SimUDP) + +The SimUDP peripheral provides a mean to transmit and receive datagrams through a UDP socket connection. + +## SimUDP Registers + +### UDP RXIP Register + +Address offset: 0x00 +Reset value: 0x0100_007F + +`Bit 31:0` IP: The IP address to listen to, in network endianness (big-endian) + +### UDP TXIP Register + +Address offset: 0x04 +Reset value: 0x0100_007F + +`Bit 31:0` IP: The IP address to send to, in network endianness (big-endian) + +### UDP RXPORT Register + +Address offset: 0x08 +Reset value: 0x0000_0000 + +`Bit 15:0` PORT: The receive port in network endianness (big-endian) + +### UDP TXPORT Register + +Address offset: 0x0C +Reset value: 0x0000_0000 + +`Bit 15:0` PORT: The transmit port in network endianness (big-endian) + +### UDP CTRL Register + +Address offset: 0x10 +Reset value: 0x0000_0000 + +`Bit 0` ENABLE: Enable the device. + +`Bit 1` RXEN: Enable the reception functionality for one frame. + +`Bit 2` TXEN: Enable the transmission functionality for one frame. + +### UDP STATUS Register + +Address offset: 0x14 +Reset value: 0x0000_0000 + +`Bit 15:0` MAXRXSIZE: Maximum number of bytes to receive. + +### UDP RXSIZE Register + +Address offset: 0x18 +Reset value: 0x0000_0000 + +`Bit 15:0` SIZE: Number of bytes received. + +### UDP TXSIZE Register + +Address offset: 0x1C +Reset value: 0x0000_0000 + +`Bit 15:0` SIZE: Number of bytes to transmit. + +### UDP RXFIFO Register[0:255] + +Address offset: 0x30 +Reset value: 0x0000_0000 + +`Bit 31:0` DATA + +### UDP TXSIZE Register[0:255] + +Address offset: 0x130 +Reset value: 0x0000_0000 + +`Bit 31:0` DATA + + +## Programming Model + +``` +void UDP_enable() { + regwrite32(rxip, rxip); + regwrite32(txip, txip); + regwrite32(rxport, rxport); + regwrite32(txport, txport); + regwrite32(ctrl, 0x01); // enables the device +} +``` +``` +void UDP_config_recv_size(uint32_t size){ + regwrite32(rx_size, size); +} +``` +``` +void UDP_receive_frame(){ + regwrite32(ctrl, 0x01 << 1); // turns on the udp rx flag for a frame + while (!rx_status); // poll untile done +} +``` \ No newline at end of file diff --git a/src/sim_udp.cc b/src/sim_udp.cc new file mode 100644 index 0000000..b78b0c0 --- /dev/null +++ b/src/sim_udp.cc @@ -0,0 +1,256 @@ +#include "sim_udp.h" + +// constructor +sim_udp_t::sim_udp_t(abstract_interrupt_controller_t *intctrl, reg_t int_id) { + this->interrupt_id = int_id; + this->intctrl = intctrl; + + this->udp.rx_addr.sin_family = AF_INET; + this->udp.tx_addr.sin_family = AF_INET; + + this->reg_ctrl = 0; + this->reg_status = 0; + this->reg_rxsize = 0; + this->reg_txsize = 0; + + this->enabled = 0; + this->rx_flag = 0; + this->tx_flag = 0; + + + // Create socket file descriptor + this->udp.sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (this->udp.sockfd < 0) { + printf(" [ERROR]: socket creation failed\n"); + exit(1); + } + + std::thread rx_thread(&sim_udp_t::udp_receive_handler, this); + std::thread tx_thread(&sim_udp_t::udp_send_handler, this); + + rx_thread.detach(); + tx_thread.detach(); + + this->reg_rx_status = 0x00; + this->reg_tx_status = 0x00; +} + +void sim_udp_t::udp_receive_handler() { + printf(" [INFO]: UDP Rx thread started\n"); + + while (1) { + if (this->rx_flag) { + int n = recvfrom(this->udp.sockfd, (void *)this->rx_buffer, this->reg_rxsize, MSG_WAITALL, NULL, 0); + + if (n) { + rx_fifo_mutex.lock(); + for (int i = 0; i < n; i += 1) { + this->rx_fifo.push(this->rx_buffer[i]); + } + rx_fifo_mutex.unlock(); + + this->reg_rxsize = n; + this->reg_rx_status = 0x01; + + this->rx_flag = 0; + } + } + } +} + +void sim_udp_t::udp_send_handler() { + + printf(" [INFO]: UDP Tx thread started\n"); + + while (1) { + if (this->tx_flag && this->tx_fifo.size() >= this->reg_txsize) { + printf(" [INFO]: UDP Tx to (%s, %d) with data size: %d\n", + inet_ntoa(this->udp.tx_addr.sin_addr), + ntohs(this->udp.tx_addr.sin_port), + this->reg_txsize + ); + + for (int i = 0; i < this->reg_txsize; i += 1) { + this->tx_buffer[i] = this->tx_fifo.front(); + this->tx_fifo.pop(); + } + + sendto( + this->udp.sockfd, + (uint8_t *)this->tx_buffer, this->reg_txsize, 0, + (const struct sockaddr *)&this->udp.tx_addr, sizeof(this->udp.tx_addr) + ); + + this->reg_tx_status = 0x01; + this->tx_flag = 0; + } + } +} + +void sim_udp_t::udp_enable() { + if (this->enabled) { + return; + } + + ssize_t status = bind(this->udp.sockfd, (const struct sockaddr *)&this->udp.rx_addr, sizeof(this->udp.rx_addr)); + if (status < 0) { + printf(" [ERROR]: bind failed\n"); + } + printf(" [INFO]: bind success\n"); + this->enabled = 1; + +} + +bool sim_udp_t::load(reg_t addr, size_t len, uint8_t* bytes) { + // if illegal address or length, return false + if (addr >= 0x1000 || len > 4) return false; + + uint32_t r = 0; + switch (addr) { + case UDP_RXFIFO_DATA: + if (this->rx_fifo.size() == 0) { + // set empty flag + r = 0x80000000; + } + else { + r = this->rx_fifo.front(); + this->rx_fifo.pop(); + } + break; + + case UDP_RXFIFO_SIZE: + r = this->rx_fifo.size(); + break; + + case UDP_TXFIFO_DATA: + r = (this->tx_fifo.size() == FIFO_SIZE ? 0x80000000 : 0x00) | this->tx_fifo.back(); + break; + + case UDP_TXFIFO_SIZE: + r = this->tx_fifo.size(); + break; + + case UDP_RX_STATUS: + r = this->reg_rx_status; + break; + case UDP_TX_STATUS: + r = this->reg_tx_status; + break; + default: printf("LOAD -- ADDR=0x%lx LEN=%lu\n", addr, len); abort(); + } + memcpy(bytes, &r, len); + return true; +} + +bool sim_udp_t::store(reg_t addr, size_t len, const uint8_t* bytes) { + // if illegal address or length, return false + if (addr >= 0x1000 || len > 4) return false; + + switch (addr) { + case UDP_RXIP: + this->udp.rx_addr.sin_addr.s_addr = *((uint32_t *)bytes); + return true; + + case UDP_TXIP: + this->udp.tx_addr.sin_addr.s_addr = *((uint32_t *)bytes); + return true; + + case UDP_RXPORT: + this->udp.rx_addr.sin_port = *((uint16_t *)bytes); + return true; + + case UDP_TXPORT: + this->udp.tx_addr.sin_port = *((uint16_t *)bytes); + return true; + + case UDP_CTRL: + if (READ_BITS(bytes[0], (1 << 0))) { + this->udp_enable(); + } + if (READ_BITS(bytes[0], (1 << 1))) { + this->rx_flag = 1; + } + if (READ_BITS(bytes[0], (1 << 2))) { + this->tx_flag = 1; + } + return true; + + case UDP_RXSIZE: + this->reg_rxsize = *((uint32_t *)bytes); + return true; + + case UDP_TXSIZE: + this->reg_txsize = *((uint32_t *)bytes); + return true; + + case UDP_TXFIFO_DATA: + this->tx_fifo.push(bytes[0]); + return true; + + case UDP_RX_STATUS: + this->reg_rx_status = 0x00; + return true; + + case UDP_TX_STATUS: + this->reg_tx_status = 0x00; + return true; + + default: printf("STORE -- ADDR=0x%lx LEN=%lu\n", addr, len); abort(); + } +} + +void sim_udp_t::tick(reg_t UNUSED rtc_ticks) { + // if (rx_fifo.size() >= 1) return; + // int rc = canonical_terminal_t::read(); + // if (rc < 0) return; + // rx_fifo.push((uint8_t)rc); + // update_interrupts(); + +} + +int SimUDP_parseFDT(const void *fdt, reg_t *address, + const char *compatible) { + int nodeoffset, len, rc; + const fdt32_t *reg_p; + + nodeoffset = fdt_node_offset_by_compatible(fdt, -1, compatible); + if (nodeoffset < 0) + return nodeoffset; + + rc = fdt_get_node_addr_size(fdt, nodeoffset, address, NULL, "reg"); + if (rc < 0 || !address) + return -ENODEV; + + return 0; +} + + +// This function parses an instance of this device from the FDT +// An FDT node for a device should encode, at minimum, the base address for the device +sim_udp_t* SimUDP_parseFromFDT(const void* fdt, const sim_t* sim, reg_t* base, std::vector sargs) { + if (SimUDP_parseFDT(fdt, base, "ucbbar,sim_udp") == 0) { + printf("Found SimUDP at 0x%lx\n", *base); + return new sim_udp_t(sim->get_intctrl(), 1); + } else { + return nullptr; + } +} + +std::string SimUDP_generateDTS(const sim_t* sim, const std::vector& args) { + std::stringstream s; + s << std::hex + << " udp: udp@" << UDP_BASE << " {\n" + " compatible = \"ucbbar,sim_udp\";\n" + " interrupt-parent = <&PLIC>;\n" + " interrupts = <" << std::dec << 2; + reg_t blkdevbs = UDP_BASE; + reg_t blkdevsz = UDP_SIZE; + s << std::hex << ">;\n" + " reg = <0x" << (blkdevbs >> 32) << " 0x" << (blkdevbs & (uint32_t)-1) << + " 0x" << (blkdevsz >> 32) << " 0x" << (blkdevsz & (uint32_t)-1) << ">;\n" + " };\n"; + return s.str(); +} + +REGISTER_DEVICE(sim_udp, SimUDP_parseFromFDT, SimUDP_generateDTS); diff --git a/src/sim_udp.h b/src/sim_udp.h new file mode 100644 index 0000000..f2ec9e3 --- /dev/null +++ b/src/sim_udp.h @@ -0,0 +1,107 @@ +#ifndef _SIM_UDP_H +#define _SIM_UDP_H + +#include +#include +#include +#include +#include + + + +#include +#include +#include +#include +#include +#include +#include + + +/* ================ Bit Operation definitions ================ */ +#define SET_BITS(REG, BIT) ((REG) |= (BIT)) +#define CLEAR_BITS(REG, BIT) ((REG) &= ~(BIT)) +#define READ_BITS(REG, BIT) ((REG) & (BIT)) +#define WRITE_BITS(REG, CLEARMASK, SETMASK) ((REG) = (((REG) & (~(CLEARMASK))) | (SETMASK))) + + + +#define UDP_BASE 0x10001000 +#define UDP_SIZE 0x1000 + +#define UDP_RXIP 0x00 +#define UDP_TXIP 0x04 +#define UDP_RXPORT 0x08 +#define UDP_TXPORT 0x0C +#define UDP_CTRL 0x10 +#define UDP_STATUS 0x14 +#define UDP_RXSIZE 0x18 +#define UDP_TXSIZE 0x1C + +#define UDP_RXFIFO_DATA 0x20 +#define UDP_RXFIFO_SIZE 0x24 +#define UDP_RXFIFO_READY 0x28 + +#define UDP_TXFIFO_DATA 0x30 +#define UDP_TXFIFO_SIZE 0x34 +#define UDP_TXFIFO_READY 0x38 + +#define UDP_RX_STATUS 0x40 +#define UDP_TX_STATUS 0x44 + + +#define FIFO_SIZE 16 + + +typedef struct { + int sockfd; + pthread_t thread_id; + struct sockaddr_in tx_addr; + struct sockaddr_in rx_addr; +} UDPSocket; + +class sim_udp_t : public abstract_device_t { +public: + + sim_udp_t(abstract_interrupt_controller_t *intctrl, reg_t int_id); + bool load(reg_t addr, size_t len, uint8_t* bytes) override; + bool store(reg_t addr, size_t len, const uint8_t* bytes) override; + void tick(reg_t UNUSED rtc_ticks) override; + +private: + uint32_t reg_ctrl; + uint32_t reg_status; + uint32_t reg_rxsize; + uint32_t reg_txsize; + uint8_t rx_buffer[FIFO_SIZE]; + uint8_t tx_buffer[FIFO_SIZE]; + std::queue rx_fifo; + std::mutex rx_fifo_mutex; + std::queue tx_fifo; + std::mutex tx_fifo_mutex; + uint8_t rx_fifo_to_pop; + uint8_t tx_fifo_to_push; + + uint32_t ie; + uint32_t ip; + + reg_t interrupt_id; + abstract_interrupt_controller_t *intctrl; + + UDPSocket udp; + + uint8_t reg_rx_status; + uint8_t reg_tx_status; + + uint8_t enabled; + uint8_t rx_flag; + uint8_t tx_flag; + + void udp_enable(); + void udp_set_rx_flag(); + void udp_set_tx_flag(); + void udp_receive_handler(); + void udp_send_handler(); +}; + +#endif // _SIM_UDP_H