Skip to content

Commit 800a0d4

Browse files
committed
Fix slink_32b_elf_preload
1 parent f3306d2 commit 800a0d4

File tree

1 file changed

+78
-40
lines changed

1 file changed

+78
-40
lines changed

target/sim/src/tb_picobello_top.sv

Lines changed: 78 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -62,56 +62,94 @@ 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 axi_pkg::size_t 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+
// Beat geometry
77+
const int beat_bytes = fix.vip.AxiStrbWidth;
78+
const int beat_mask = beat_bytes - 1;
79+
80+
// Iterate beat-by-beat over the address range [addr, addr+size)
81+
addr_t first_aligned = addr_t'(addr) & ~addr_t'(beat_mask);
82+
addr_t end_addr = addr_t'(addr + size);
83+
addr_t last_aligned = addr_t'((end_addr - 1) & ~addr_t'(beat_mask));
84+
85+
// Running index into bytes[]: "how many bytes have we already consumed?"
86+
longint base_idx = 0;
87+
88+
// Accumulate beats for current 4 KiB page
89+
addr_t batch_addr = first_aligned;
90+
axi_data_t out[$]; out = {};
91+
92+
for (addr_t beat_addr = first_aligned; beat_addr <= last_aligned; beat_addr += beat_bytes) begin
93+
addr_t next_addr;
94+
bit crosses_4k_next;
95+
bit 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, $clog2(beat_bytes), 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 or this is the last beat
124+
next_addr = beat_addr + beat_bytes;
125+
crosses_4k_next = ((next_addr & 12'hFFF) == 12'h000); // next beat starts a new page
126+
last_beat_in_section = (beat_addr == last_aligned);
127+
128+
if (crosses_4k_next || last_beat_in_section) begin
129+
// Flush accumulated beats for this page
130+
fix.vip.slink_write_beats(batch_addr, out.size(), out);
131+
out = {};
132+
batch_addr = next_addr;
133+
end
134+
end
135+
endtask
136+
65137
task automatic slink_32b_elf_preload(input string binary, output bit [63:0] entry);
66138
longint sec_addr, sec_len;
67139
$display("[SLINK] Preloading ELF binary: %s", binary);
68140
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;
141+
142+
while (fix.vip.get_section(sec_addr, sec_len)) begin
143+
byte bf[] = new [sec_len];
74144
$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
145+
if (fix.vip.read_section(sec_addr, bf, sec_len)) $fatal(1, "[SLINK] Failed to read ELF section!");
146+
slink_write_generic(sec_addr, sec_len, bf);
109147
end
148+
110149
void'(fix.vip.get_entry(entry));
111150
$display("[SLINK] Preload complete");
112151
endtask
113152

114-
115153
initial begin
116154
// Fetch plusargs or use safe (fail-fast) defaults
117155
if (!$value$plusargs("BOOTMODE=%d", boot_mode)) boot_mode = 0;

0 commit comments

Comments
 (0)