@@ -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 " 
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)" 
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%% )" 
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 " =  0 ;
0 commit comments