@@ -62,56 +62,114 @@ module tb_picobello_top;
6262 $display (" [JTAG] Preload complete" );
6363 endtask
6464
65+ // Handles misalignments and 4KiB crossings
66+ task automatic slink_write_generic (
67+ input addr_t addr,
68+ input longint size,
69+ ref byte bytes []
70+ );
71+ // Using `slink_write_beats`, writes must be beat-aligned and beat-sized (strobing is not
72+ // possible). If we have a misaligned transfer of arbitrary size we may have at most two
73+ // incomplete beats (start and end) and one misaligned beat (start). In case of an incomplete
74+ // beat we read-modify-write the full beat.
75+
76+ // Burst and beat geometry
77+ const int beat_bytes = fix.vip.AxiStrbWidth;
78+ const int beat_mask = beat_bytes - 1 ;
79+ const int SlinkBurstBeats = fix.vip.SlinkBurstBytes / beat_bytes;
80+
81+ // Iterate beat-by-beat over the address range [addr, addr+size)
82+ addr_t first_aligned = addr_t ' (addr) & ~ addr_t ' (beat_mask);
83+ addr_t end_addr = addr_t ' (addr + size);
84+ addr_t last_aligned = addr_t ' ((end_addr - 1 ) & ~ addr_t ' (beat_mask));
85+
86+ // Running index into bytes[]: "how many bytes have we already consumed?"
87+ longint base_idx = 0 ;
88+
89+ // Accumulate beats for current 4 KiB page
90+ addr_t batch_addr = first_aligned;
91+ axi_data_t out[$]; out = {} ;
92+
93+ for (addr_t beat_addr = first_aligned; beat_addr <= last_aligned; beat_addr + = beat_bytes) begin
94+ addr_t next_addr;
95+ bit crosses_4k_next, exceeds_burst_length, last_beat_in_section;
96+
97+ // Window of the current beat that has to be written
98+ int start_off = (beat_addr == first_aligned) ? int '(addr & beat_mask) : 0 ;
99+ int end_off_excl = (beat_addr == last_aligned) ? int '(end_addr - last_aligned) : beat_bytes;
100+ int win_len = end_off_excl - start_off;
101+
102+ // Compose beat
103+ axi_data_t beat = '0 ;
104+ if (win_len == beat_bytes && start_off == 0 ) begin
105+ // FULL BEAT: write directly, no RMW
106+ for (int e = 0 ; e < beat_bytes; e++ ) begin
107+ beat[8 * e + : 8 ] = bytes[base_idx + e];
108+ end
109+ end else begin
110+ // PARTIAL BEAT: RMW
111+ axi_data_t rd[$];
112+ fix.vip.slink_read_beats (beat_addr, fix.vip.AxiStrbBits, 0 , rd);
113+ beat = rd[0 ];
114+ for (int i = 0 ; i < win_len; i++ ) begin
115+ beat[8 * (start_off + i) + : 8 ] = bytes[base_idx + i];
116+ end
117+ end
118+
119+ // Accumulate and advance
120+ out.push_back (beat);
121+ base_idx + = win_len;
122+
123+ // Decide if the next beat would cross a 4 KiB boundary, exceed maximum burst length
124+ // or this is the last beat
125+ next_addr = beat_addr + win_len;
126+ crosses_4k_next = ((next_addr & 12'hFFF ) == 12'h000 ); // next beat starts a new page
127+ exceeds_burst_length = (out.size () == SlinkBurstBeats);
128+ last_beat_in_section = (beat_addr == last_aligned);
129+
130+ if (crosses_4k_next || exceeds_burst_length || last_beat_in_section) begin
131+ // Flush accumulated beats for this page
132+ fix.vip.slink_write_beats (batch_addr, fix.vip.AxiStrbBits, out);
133+ out = {} ;
134+ batch_addr = next_addr;
135+ end
136+ end
137+ endtask
138+
65139 task automatic slink_32b_elf_preload (input string binary, output bit [63 : 0 ] entry);
140+
66141 longint sec_addr, sec_len;
142+
143+ // bit [32] mask;
144+ // axi_data_t rd[$];
145+ // axi_data_t wr[$] = {{'0, 32'hdeadbeef}};
146+ // $display("[SLINK] StrbBits and StrbWidth: %0d / %0d", fix.vip.AxiStrbBits, fix.vip.AxiStrbWidth);
147+ // $display("[SLINK] Testing write/read");
148+ // $display("[SLINK] Write 0xdeadbeef to 0x70002000");
149+ // fix.vip.slink_write_beats(32'h70002000, fix.vip.AxiStrbBits, wr);
150+ // fix.vip.slink_read_beats(32'h70002000, fix.vip.AxiStrbBits, 0, rd);
151+ // $display("[SLINK] Read 0x%h at 0x70002000", rd[0]);
152+
67153 $display (" [SLINK] Preloading ELF binary: %s " , binary);
68154 if (fix.vip.read_elf (binary)) $fatal (1 , " [SLINK] Failed to load ELF!" );
69- while (fix.vip.get_section (
70- sec_addr, sec_len
71- )) begin
72- byte bf [] = new [sec_len];
73- int burst_len;
155+
156+ while (fix.vip.get_section (sec_addr, sec_len)) begin
157+ byte bf[] = new [sec_len];
74158 $display (" [SLINK] Preloading section at 0x%h (%0d bytes)" , sec_addr, sec_len);
75- if (fix.vip.read_section (sec_addr, bf, sec_len))
76- $fatal (1 , " [SLINK] Failed to read ELF section!" );
77- // Write section in bursts <= SlinkBurstBytes that never cross a 4 KiB page
78- for (longint sec_offs = 0 ; sec_offs < sec_len; sec_offs + = burst_len) begin
79- longint sec_left, page_left;
80- axi_data_t beats [$];
81- int bus_offs;
82- addr_t addr_cur = sec_addr + sec_offs;
83- if (sec_offs != 0 ) begin
84- $display (" [SLINK] - %0d /%0d bytes (%0d%% )" , sec_offs, sec_len,
85- sec_offs * 100 / (sec_len > 1 ? sec_len - 1 : 1 ));
86- end
87- // By default the burst length is SlinkBurstBytes
88- burst_len = fix.vip.SlinkBurstBytes;
89- // Cut the burst length if it exceeds the remaining section length
90- // or it crosses a 4 KiB page boundary
91- sec_left = sec_len - sec_offs;
92- page_left = 4096 - (addr_cur & 12'hFFF );
93- if (burst_len > sec_left) burst_len = int '(sec_left);
94- if (burst_len > page_left) burst_len = int '(page_left);
95- bus_offs = addr_cur[fix.vip.AxiStrbBits- 1 : 0 ];
96-
97- // If the address is not aligned subtract the offset from the burst length to avoid an additional write
98- burst_len = burst_len - bus_offs;
99- // Assemble beats, handling unaligned start in the first beat
100- for (int b = - bus_offs; b < burst_len; b + = fix.vip.AxiStrbWidth) begin
101- axi_data_t beat = '0 ;
102- for (int e = 0 ; e < fix.vip.AxiStrbWidth; ++ e)
103- if (b + e >= 0 && b + e < burst_len) beat[8 * e+ : 8 ] = bf[sec_offs+ b+ e];
104- beats.push_back (beat);
105- end
106- // Address must be beat‑aligned for slink_write_beats
107- fix.vip.slink_write_beats (addr_cur - bus_offs, fix.vip.AxiStrbBits, beats);
108- end
159+ if (fix.vip.read_section (sec_addr, bf, sec_len)) $fatal (1 , " [SLINK] Failed to read ELF section!" );
160+ slink_write_generic (sec_addr, sec_len, bf);
109161 end
162+
163+ // mask = {fix.vip.AxiStrbBits{1'b1}};
164+ // fix.vip.slink_read_beats(32'h70002df4 & ~mask, fix.vip.AxiStrbBits, 0, rd);
165+ // $display("0x%h", rd[0]);
166+ // fix.vip.slink_read_beats(32'h70002000 & ~mask, fix.vip.AxiStrbBits, 0, rd);
167+ // $display("0x%h", rd[0]);
168+
110169 void '(fix.vip.get_entry (entry));
111170 $display (" [SLINK] Preload complete" );
112171 endtask
113172
114-
115173 initial begin
116174 // Fetch plusargs or use safe (fail-fast) defaults
117175 if (! $value$plusargs (" BOOTMODE=%d " , boot_mode)) boot_mode = 0 ;
0 commit comments