SDA Ethernet encapsulation with ChatGPT o4
The Space Development Agency released the Optical Communications Terminal Standard Version 4.0 on June 28, 2024.
I wanted to test ChatGPT-4.0 (ChatGPT 4o) to see how well it could code the Ethernet encapsulation section (3.4.8). I already had a rough idea of what needed to be done, as I have my own standard for FSO (free-space optical) communications that I've used in the past. To test ChatGPT, I fed it the SDA standard and asked it to review the document. Then, for section 3.4.8 on Ethernet encapsulation, I instructed it to act as a VHDL design engineer and code a VHDL module that accepts input as AXI stream Ethernet packets (with data 32 bits wide) and converts the output into an FSO frame.
Over the past several days, I have been working with ChatGPT during my spare time, and these are the results.
The first iteration from ChatGPT produced a single module that lacked memory, did not count bytes, and did not have any length count for either the FSO payload header or the FSO packet header. On the plus side, it provided a template that contained much of the textual information needed. It used the VHDL libraries standard_logic_1164, standard_logic_arith, and standard_logic_unsigned.
One obvious issue with ChatGPT’s response is that it had difficulty inferring derived requirements from the specification. For example, as background information, Ethernet packets do not include a length indicator in the Ethernet header. Because of this, if the length of the Ethernet packet is needed, the bytes must be counted as they come in—and, if the packet length is to be prepended, the entire packet may need to be stored. In the SDA spec, this is handled by having the first word consist of a magic number concatenated with a length number, forming a 32-bit word (the magic number is 16 bits, the length number is 14 bits, and two bits are reserved to make up 32 bits). This means that, to populate the FSO packet header, the entire Ethernet packet must be stored so that the packet header can be prepended.
In my next prompt, I informed ChatGPT that although the length field was present in the code, it was initialized to zero and never calculated. I also explained that, according to the SDA spec, there are two length indicators: one for the FSO payload and one for the FSO packet.
In this iteration, ChatGPT only encapsulated one Ethernet packet, leaving the remainder of the FSO payload unused. I pointed this out and also indicated that the Ethernet packet data was not being stored—it should be stored in a BRAM. I further asked where the length field was calculated and requested that the code check for oversized MTU lengths in Ethernet packets.
Regarding the FSO packet header, ChatGPT had created a constant called "fso packet header," but this value was dynamically updated with respect to the Ethernet packets. I clarified that there are two length fields (one for the packet and one for the FSO payload) and had it rename the length field for the packet to "pkt_length."
The code was mainly centered around a state machine that handled various aspects of the FSO payload. In the "packet header" state, it output fso_tdata as the concatenation of the magic number and the length field. In its first iteration for this state, the module changed this value three times before moving on to the next state. I asked it to remove these redundant data replications, and it struggled with this task. I ultimately wanted the header to be produced by a continuous assignment based on the concatenation of the length field and the magic number; while ChatGPT generated this continuous assignment, it did not remove the redundant assignments within the state machine. When I finally managed to remove the redundant assignments in the state machine, the continuous assignment for the header outside the state machine was also removed.
As part of its response, ChatGPT provides next steps and suggested enhancements. A couple of the things it did not implement include:
• Code for CRC-32 computation—which I can understand.
• Integration of Ethernet frame segmentation logic specifically for this module—which I would expect it to handle.
• A test bench—which, although desirable, was not included.
While I can overlook the absence of CRC computation or a test bench, the glaring error in this iteration is the Ethernet frame segmentation.
With all this back and forth, I still haven’t achieved a working SDA code base for Ethernet encapsulation. The next iteration would involve splitting up the Ethernet packet wrapping: one module would accept an Ethernet packet and prepend the packet header, and then this packet would be passed to an FSO payload wrapper that collects Ethernet packets and forms an FSO payload. Each module would need to allocate memory corresponding to the maximum Ethernet packet size and the FSO payload.
As it stands, the current code appears to have the packet length corrupted each time a new Ethernet packet arrives. While the code may be usable as a starting point it needs to be thoroughly simulated in a rigorous test bench. There are still several issues with the code and certainly can not be used as is. I would split this up into two entities that perform the functions of Ethernet wrapping then one to collect the packets into a FSO frame.
below is the last output from the ChatGPT chat:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.STD_LOGIC_MISC.ALL;
entity Ethernet_AXISTREAM_to_FSO is
Port (
clk : in std_logic;
reset : in std_logic;
s_tvalid : in std_logic;
s_tready : out std_logic;
s_tdata : in std_logic_vector(31 downto 0);
s_tkeep : in std_logic_vector(3 downto 0);
s_tlast : in std_logic;
fso_tvalid : out std_logic;
fso_tdata : out std_logic_vector(31 downto 0);
fso_tkeep : out std_logic_vector(3 downto 0);
fso_tlast : out std_logic
);
end Ethernet_AXISTREAM_to_FSO;
architecture Behavioral of Ethernet_AXISTREAM_to_FSO is
type state_type is (IDLE, HEADER, PACKET_HEADER, PAYLOAD, CRC, DONE);
signal state : state_type := IDLE;
signal seq_num : std_logic_vector(9 downto 0) := (others => '0'); -- 10-bit sequence number
signal length : std_logic_vector(13 downto 0) := (others => '0'); -- Length field
signal byte_count : integer := 0;
signal last_flag : std_logic;
constant MAX_FRAME_SIZE : integer := 1500; -- Ethernet MTU
constant MAX_FSO_PAYLOAD_SIZE : integer := 4096; -- Max FSO frame payload size
constant FSO_PAYLOAD_HEADER : std_logic_vector(31 downto 0) := x"AB000000"; -- Magic Number + placeholders
signal pkt_length : std_logic_vector(13 downto 0) := (others => '0');
signal FSO_PACKET_HEADER : std_logic_vector(31 downto 0); -- Magic Number + placeholders
signal crc_reg : std_logic_vector(31 downto 0); -- Placeholder for CRC computation
-- BRAM Memory Declaration
type bram_type is array (0 to 1023) of std_logic_vector(31 downto 0);
signal bram : bram_type := (others => (others => '0'));
signal bram_write_addr : integer range 0 to 1023 := 0;
signal bram_read_addr : integer range 0 to 1023 := 0;
signal bram_we : std_logic := '0';
signal fso_byte_count : integer := 0;
begin
process (clk)
begin
if rising_edge(clk) then
if reset = '1' then
state <= IDLE;
s_tready <= '1';
fso_tvalid <= '0';
seq_num <= (others => '0');
byte_count <= 0;
fso_byte_count <= 0;
bram_write_addr <= 0;
bram_read_addr <= 0;
else
case state is
when IDLE =>
pkt_length <= (others => '0');
if s_tvalid = '1' and s_tready = '1' then
-- Store data in BRAM
bram(bram_write_addr) <= s_tdata;
bram_write_addr <= bram_write_addr + 1;
byte_count <= byte_count + 4;
fso_byte_count <= fso_byte_count + 4;
last_flag <= s_tlast;
if s_tlast = '1' then
if fso_byte_count + byte_count <= MAX_FSO_PAYLOAD_SIZE then
length <= conv_std_logic_vector(fso_byte_count + byte_count, 14);
state <= IDLE;
else
length <= conv_std_logic_vector(fso_byte_count, 14);
state <= HEADER;
end if;
end if;
end if;
when HEADER =>
fso_tvalid <= '1';
fso_tdata <= FSO_PAYLOAD_HEADER(31 downto 24) & seq_num & length;
fso_tkeep <= "1111";
state <= PACKET_HEADER;
when PACKET_HEADER =>
pkt_length <= conv_std_logic_vector(byte_count, 14);
fso_tvalid <= '1';
fso_tdata <= x"CDEF" & "00" & pkt_length;
fso_tkeep <= "1111";
state <= PAYLOAD;
when PAYLOAD =>
if byte_count < MAX_FSO_PAYLOAD_SIZE then
fso_tvalid <= '1';
fso_tdata <= bram(bram_read_addr);
fso_tkeep <= "1111";
bram_read_addr <= bram_read_addr + 1;
byte_count <= byte_count - 4;
if last_flag = '1' and byte_count = 0 then
state <= CRC;
end if;
else
-- Segment FSO Frame
state <= HEADER;
end if;
when CRC =>
fso_tvalid <= '1';
fso_tdata <= crc_reg;
fso_tkeep <= "1111";
state <= DONE;
when DONE =>
fso_tvalid <= '0';
state <= IDLE;
seq_num <= seq_num + 1; -- Increment sequence number
byte_count <= 0;
fso_byte_count <= 0;
bram_write_addr <= 0;
bram_read_addr <= 0;
when others =>
state <= IDLE;
byte_count <= 0;
fso_byte_count <= 0;
end case;
end if;
end if;
end process;
end Behavioral;
- Comments
- Write a Comment Select to add a comment
To post reply to a comment, click on the 'reply' button attached to each comment. To post a new comment (not a reply to a comment) check out the 'Write a Comment' tab at the top of the comments.
Please login (on the right) if you already have an account on this platform.
Otherwise, please use this form to register (free) an join one of the largest online community for Electrical/Embedded/DSP/FPGA/ML engineers: