From a5afad69927f281dd5903ed09e4dd55f88c79ac4 Mon Sep 17 00:00:00 2001 From: Fischer Moseley <42497969+fischermoseley@users.noreply.github.com> Date: Sun, 4 Feb 2024 14:29:39 -0800 Subject: [PATCH] io core with ethernet working! --- .gitignore | 12 +++++- project_0/stream.py | 2 +- project_1/manta.yaml | 16 ++++++- project_1/stream.py | 49 +++++++++++++++------ project_1/test.py | 5 +++ src/manta/ethernet.py | 87 +++++++++++++++++++++++++++++++++++--- src/manta/uart/__init__.py | 5 ++- src/manta/utils.py | 1 + 8 files changed, 151 insertions(+), 26 deletions(-) create mode 100644 project_1/test.py diff --git a/.gitignore b/.gitignore index e85d6af..5dc2efa 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,14 @@ build/ *.v *.sv *.vcd -*.out \ No newline at end of file +*.out + +# Vivado files from the occasional debugging sesh +*.log +*.jou +*.rpt +*.bin +*.bit +*.out +*.xdc +.Xil/* \ No newline at end of file diff --git a/project_0/stream.py b/project_0/stream.py index 038d43e..82a17c8 100644 --- a/project_0/stream.py +++ b/project_0/stream.py @@ -65,4 +65,4 @@ if __name__ == "__main__": # for _ in range(64): # send_to_host_test("192.168.0.107", 42069) # leds_test(ip_address, udp_port) - send_variable_length_test() + # send_variable_length_test() diff --git a/project_1/manta.yaml b/project_1/manta.yaml index 1e9cc7a..23f7b6d 100644 --- a/project_1/manta.yaml +++ b/project_1/manta.yaml @@ -10,6 +10,18 @@ cores: led: 16 ethernet: - desired_fpga_ip: "192.168.0.110" - host_ip: "192.168.0.107" + phy: LiteEthPHYRMII + vendor: xilinx + toolchain: vivado + + refclk_freq: 50e6 + clk_freq: 50e6 + + fpga_ip_addr: "192.168.0.110" + host_ip_addr: "192.168.0.100" udp_port: 42069 + + core: udp # we handle this + mac_address: # this should be optional + dhcp: # should be optional, default to true? + data_width: 32 # using DHCP will force this to 32, but for ease of use we should design for that even for static IP devices diff --git a/project_1/stream.py b/project_1/stream.py index 5a3c542..ae56e49 100644 --- a/project_1/stream.py +++ b/project_1/stream.py @@ -1,17 +1,40 @@ -from manta import Manta import socket -import time - -if __name__ == "__main__": - ip_address = "192.168.0.110" - udp_port = 42069 +def write(addrs, datas): + bytes_out = b"" + for addr, data in zip(addrs, datas): + bytes_out += int(1).to_bytes(4, byteorder="little") + bytes_out += int(addr).to_bytes(2, byteorder="little") + bytes_out += int(data).to_bytes(2, byteorder="little") sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.sendto(bytes_out, (fpga_ip_addr, udp_port)) - for i in range(256): - data = [0, 0] - data = [int(d).to_bytes(4, byteorder="big") for d in data] - data = b"".join(data) - print(data) - sock.sendto(data, (ip_address, udp_port)) - # time.sleep(0.2) +def read(addrs): + bytes_out = b"" + for addr in addrs: + bytes_out += int(0).to_bytes(4, byteorder="little") + bytes_out += int(addr).to_bytes(2, byteorder="little") + bytes_out += int(0).to_bytes(2, byteorder="little") + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind((host_ip_addr, udp_port)) + sock.sendto(bytes_out, (fpga_ip_addr, udp_port)) + data, addr = sock.recvfrom(1024) + + return int.from_bytes(data, "little") + +if __name__ == "__main__": + host_ip_addr = "192.168.0.100" + fpga_ip_addr = "192.168.0.110" + udp_port = 42069 + + for i in range(2**16): + write([0x0000],[0x0000]) + write([0x0000],[0x0001]) + write([0x0000],[0x0000]) + write([0x0002],[i]) + # print(read([0x0002])) + # write([0x0002],[0b0101_0101_0101_0101]) + # write([0x0000],[0x0000]) + # write([0x0000],[0x0001]) + # write([0x0000],[0x0000]) diff --git a/project_1/test.py b/project_1/test.py new file mode 100644 index 0000000..3a725ad --- /dev/null +++ b/project_1/test.py @@ -0,0 +1,5 @@ +from manta import Manta +m = Manta("manta.yaml") + +print(bin(m.io_core.get_probe("sw"))) +m.io_core.set_probe("led", 4) \ No newline at end of file diff --git a/src/manta/ethernet.py b/src/manta/ethernet.py index ae348b5..9f174fd 100644 --- a/src/manta/ethernet.py +++ b/src/manta/ethernet.py @@ -1,11 +1,12 @@ from amaranth import * from manta.utils import * +import socket class EthernetInterface(Elaboratable): def __init__(self, config): - self.desired_fpga_ip = config["desired_fpga_ip"] - self.host_ip = config["host_ip"] + self.fpga_ip_addr = config["fpga_ip_addr"] + self.host_ip_addr = config["host_ip_addr"] self.udp_port = config["udp_port"] self.bus_i = Signal(InternalBus()) @@ -76,7 +77,7 @@ class EthernetInterface(Elaboratable): # ("o", "dhcp_ip_address", 1), ("i", "dhcp_start", self.dhcp_start), # ("o", "dhcp_timeout", 1), - ("i", "ip_address", self.binarize_ip_addr(self.desired_fpga_ip)), + ("i", "ip_address", self.binarize_ip_addr(self.fpga_ip_addr)), # UDP Port ("i", "udp0_udp_port", self.udp_port), # UDP from host @@ -86,7 +87,7 @@ class EthernetInterface(Elaboratable): ("i", "udp0_source_ready", self.source_ready), ("o", "udp0_source_valid", self.source_valid), # UDP back to host - ("i", "udp0_ip_address", self.binarize_ip_addr(self.host_ip)), + ("i", "udp0_ip_address", self.binarize_ip_addr(self.host_ip_addr)), ("i", "udp0_sink_data", self.sink_data), ("i", "udp0_sink_last", self.sink_last), ("o", "udp0_sink_ready", self.sink_ready), @@ -111,6 +112,78 @@ class EthernetInterface(Elaboratable): return m + def read(self, addrs): + """ + Read the data stored in a set of address on Manta's internal memory. Addresses + must be specified as either integers or a list of integers. + """ + + # Handle a single integer address + if isinstance(addrs, int): + return self.read([addrs])[0] + + # Make sure all list elements are integers + if not all(isinstance(a, int) for a in addrs): + raise ValueError("Read address must be an integer or list of integers.") + + # Send read requests, and get responses + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.bind((self.host_ip_addr, self.udp_port)) + chunk_size = 128 + addr_chunks = split_into_chunks(addrs, chunk_size) + datas = [] + + for addr_chunk in addr_chunks: + bytes_out = b"" + for addr in addr_chunk: + bytes_out += int(0).to_bytes(4, byteorder="little") + bytes_out += int(addr).to_bytes(2, byteorder="little") + bytes_out += int(0).to_bytes(2, byteorder="little") + + sock.sendto(bytes_out, (self.fpga_ip_addr, self.udp_port)) + data, addr = sock.recvfrom(4 * chunk_size) + + # Split into groups of four bytes + datas += [int.from_bytes(d, "little") for d in split_into_chunks(data, 4)] + + return datas + + def write(self, addrs, datas): + """ + Write the provided data into the provided addresses in Manta's internal memory. + Addresses and data must be specified as either integers or a list of integers. + """ + + # Handle a single integer address and data + if isinstance(addrs, int) and isinstance(datas, int): + return self.write([addrs], [datas]) + + # Make sure address and datas are all integers + if not isinstance(addrs, list) or not isinstance(datas, list): + raise ValueError( + "Write addresses and data must be an integer or list of integers." + ) + + if not all(isinstance(a, int) for a in addrs): + raise ValueError("Write addresses must be all be integers.") + + if not all(isinstance(d, int) for d in datas): + raise ValueError("Write data must all be integers.") + + # Since the FPGA doesn't issue any responses to write requests, we + # the host's input buffer isn't written to, and we don't need to + # send the data as chunks as the to avoid overflowing the input buffer. + + # Encode addrs and datas into write requests + bytes_out = b"" + for addr, data in zip(addrs, datas): + bytes_out += int(1).to_bytes(4, byteorder="little") + bytes_out += int(addr).to_bytes(2, byteorder="little") + bytes_out += int(data).to_bytes(2, byteorder="little") + + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + sock.sendto(bytes_out, (self.fpga_ip_addr, self.udp_port)) + class UDPSourceBridge(Elaboratable): def __init__(self): @@ -142,7 +215,7 @@ class UDPSourceBridge(Elaboratable): m.d.sync += self.bus_o.data.eq(self.data_i[16:]) m.d.sync += self.bus_o.rw.eq(rw_buf) m.d.sync += self.bus_o.valid.eq(1) - # m.d.sync += self.bus_o.last.eq(self.last_i) + m.d.sync += self.bus_o.last.eq(self.last_i) return m @@ -163,9 +236,9 @@ class UDPSinkBridge(Elaboratable): m.d.sync += self.last_o.eq(0) m.d.sync += self.valid_o.eq(0) - with m.If(self.bus_i.valid): + with m.If( (self.bus_i.valid) & (~self.bus_i.rw)): m.d.sync += self.data_o.eq(self.bus_i.data) - # m.d.sync += self.last_o.eq(self.bus_i.last) + m.d.sync += self.last_o.eq(self.bus_i.last) m.d.sync += self.valid_o.eq(1) return m diff --git a/src/manta/uart/__init__.py b/src/manta/uart/__init__.py index 4cfbeef..a735897 100644 --- a/src/manta/uart/__init__.py +++ b/src/manta/uart/__init__.py @@ -179,8 +179,9 @@ class UARTInterface(Elaboratable): if not all(isinstance(d, int) for d in datas): raise ValueError("Write data must all be integers.") - # I'm not sure if it's necessary to split outputs into chunks - # I think the output buffer doesn't really drop stuff, just the input buffer + # Since the FPGA doesn't issue any responses to write requests, we + # the host's input buffer isn't written to, and we don't need to + # send the data as chunks as the to avoid overflowing the input buffer. # Encode addrs and datas into write requests bytes_out = "".join([f"W{a:04X}{d:04X}\r\n" for a, d in zip(addrs, datas)]) diff --git a/src/manta/utils.py b/src/manta/utils.py index 7aa3625..1e63bcb 100644 --- a/src/manta/utils.py +++ b/src/manta/utils.py @@ -17,6 +17,7 @@ class InternalBus(data.StructLayout): "data": 16, "rw": 1, "valid": 1, + "last": 1, } )