NVC code coverage report

File:  ../../Sources/jls_framer.vhd

     0:   ---------------------------------------------------------------------------------- 
     1:   -- Engineer:    Vitor Mendes Camilo 
     2:   -- 
     3:   -- Module Name: jls_framer - Behavioral 
     4:   -- Description: 
     5:   -- 
     6:   --   Wraps the byte-stuffed compressed payload with JPEG-LS framing markers: 
     7:   --     SOI + SOF55 + SOS  (header, 25 bytes, before data) 
     8:   --     EOI = FF D9        (footer, 2 bytes, after data) 
     9:   -- 
    10:   --   Architecture: 
    11:   --     - One byte FIFO holds payload bytes. The FF D9 footer is pushed into 
    12:   --       the FIFO right after byte_stuffer's last word on iEoi, so the output 
    13:   --       side sees footer bytes as ordinary data and oLast falls out naturally. 
    14:   --     - Header bytes come from a combinational ROM (get_header_byte). The 
    15:   --       FIFO never stores header bytes since there are too many of them and 
    16:   --       we need the FIFO to store new data as the HEADER is being sent. 
    17:   --     - Output FSM (IDLE / HEADER / DATA) selects header ROM vs FIFO per beat. 
    18:   --       Splicing handles the partial last header beat (filled from the FIFO) 
    19:   --       and the partial last data beat (cut at sEndOfImage with oLast). 
    20:   --     - byte_stuffer is never back-pressured. The FIFO is sized so that 
    21:   --       pushes always fit under correct upstream behavior. 
    22:   -- 
    23:   --   iEoi (1-cycle pulse): asserts on byte_stuffer's last word for the image. 
    24:   --   That cycle the framer pushes (iBsValidBytes byte_stuffer bytes) followed 
    25:   --   by (FF D9), and latches sEndOfImage to the offset (from FIFO head) of the 
    26:   --   trailing D9. The output FSM watches this offset; when the bytes popped 
    27:   --   this cycle reach it, oLast asserts on that beat. 
    28:   -- 
    29:   --   In-flight EoI offsets are held in a small queue (EOI_FIFO_DEPTH) so that 
    30:   --   tiny back-to-back images can have multiple D9 markers pending in the FIFO 
    31:   --   at once. Depth 3 covers the smallest line_buffer-supported image (3x1) 
    32:   --   pipelined back-to-back; overflow is caught by an assert at push time. 
    33:   -- 
    34:   --   Image dimensions are fixed at reset by the top level and read directly 
    35:   --   from the ports without registering. 
    36:   -- 
    37:   --   IN_WIDTH     : word width from byte_stuffer  (CO_BYTE_STUFFER_OUT_WIDTH) 
    38:   --   OUT_WIDTH    : final output word width        (CO_OUT_WIDTH_STD) 
    39:   --   BUFFER_BYTES : payload FIFO depth in bytes (auto-derived; see below) 
    40:   -- 
    41:   --------------------------------------------------------------------------- 
    42:   -- BUFFER_BYTES sizing (worst-case occupancy) 
    43:   --------------------------------------------------------------------------- 
    44:   --   Two regimes contribute to peak FIFO occupancy: 
    45:   -- 
    46:   --   1) iEoi cycle (single-image worst case): 
    47:   --        DATA pop fires only when vCount >= BYTES_OUT, so vCount_pre at iEoi 
    48:   --        can sit at BYTES_OUT - 1 without triggering a pop. The iEoi cycle 
    49:   --        then pushes (iBsValidBytes <= BYTES_IN) plus the 2 footer bytes: 
    50:   --          vCount_iEoi_max = BYTES_OUT - 1 + BYTES_IN + 2 
    51:   -- 
    52:   --   2) Back-to-back images (image N+1 starts pushing while N drains): 
    53:   --        From iEoi cycle T, the FF D9 marker takes 
    54:   --          D = ceil(vCount_iEoi_max / BYTES_OUT) 
    55:   --        cycles to reach the FIFO head and emit (oLast fires on the D9 beat). 
    56:   --        After that, the framer transits to HEADER and emits the 25-byte 
    57:   --        marker block over 
    58:   --          N_h = ceil(HEADER_LEN / BYTES_OUT) 
    59:   --        beats. The first (N_h - 1) HEADER beats pull bytes from the ROM and 
    60:   --        do not pop the FIFO; the final mixed beat splices ROM + FIFO bytes 
    61:   --        and pops (BYTES_OUT - HEADER_LEN mod BYTES_OUT) data bytes. 
    62:   -- 
    63:   --        Worst case: byte_stuffer begins pushing image N+1 the cycle after 
    64:   --        iEoi and continues at BYTES_IN/cycle throughout. Since BYTES_OUT is 
    65:   --        normally >= BYTES_IN, image N drains faster than N+1 accumulates, 
    66:   --        so by the time the mixed HEADER beat fires N's bytes are gone and 
    67:   --        only N+1's bytes occupy the FIFO. Peak N+1 bytes (after the mixed 
    68:   --        beat's net push): 
    69:   --          PEAK_N1 = (D + N_h) * BYTES_IN 
    70:   --                  - (BYTES_OUT - HEADER_LEN mod BYTES_OUT) 
    71:   -- 
    72:   --   General formula (peak FIFO occupancy across all phases): 
    73:   -- 
    74:   --     V_iEoi  = BYTES_OUT + BYTES_IN + 1 
    75:   --     D       = ceil(V_iEoi    / BYTES_OUT) 
    76:   --     N_h     = ceil(HEADER_LEN / BYTES_OUT) 
    77:   -- 
    78:   --     BUFFER_BYTES = max( 
    79:   --       V_iEoi,                                              (a) iEoi cycle 
    80:   --       D * BYTES_IN,                                        (b) end of N drain 
    81:   --       (D + N_h - 1) * BYTES_IN,                            (c) last full hdr beat 
    82:   --       (D + N_h) * BYTES_IN - (N_h*BYTES_OUT - HEADER_LEN)  (d) after mixed beat 
    83:   --     ) 
    84:   -- 
    85:   --   For typical configs with BYTES_IN <= BYTES_OUT, term (d) dominates. 
    86:   -- 
    87:   --   Standard config (BYTES_IN=8, BYTES_OUT=9, HEADER_LEN=25): 
    88:   --     V_iEoi = 18, D = 2, N_h = 3 
    89:   --     (a)=18, (b)=16, (c)=32, (d)=(5*8) - (3*9 - 25) = 40 - 2 = 38 
    90:   --     BUFFER_BYTES = 38 
    91:   -- 
    92:   --   This also implies that outputting the last bytes after EOI can take several 
    93:   --   cycles, the image last byte needs to be tracked to assert oLast, padding and 
    94:   --   oKeep on the correct beat. 
    95:   -- 
    96:   --   Caveat: the BUFFER_BYTES formula above assumes iReady = '1' steady-state. 
    97:   --   With sustained downstream backpressure the FIFO has no bound (no upstream 
    98:   --   stall path). A runtime assert catches overflow in simulation. 
    99:   ---------------------------------------------------------------------------------- 
   100:    
   101:   library ieee; 
   102:     use ieee.std_logic_1164.all; 
   103:     use ieee.numeric_std.all; 
   104:     use work.openjls_pkg.all; 
   105:    
   106:   library work; 
   107:     use work.olo_base_pkg_math.log2ceil; 
   108:    
   109:   entity jls_framer is 
   110:     generic ( 
   111:       BITNESS            : natural := CO_BITNESS_STD; 
   112:       IN_WIDTH           : natural := CO_BYTE_STUFFER_OUT_WIDTH; 
   113:       OUT_WIDTH          : natural := CO_OUT_WIDTH_STD; 
   114:       MAX_IMAGE_WIDTH    : natural := 4096; 
   115:       MAX_IMAGE_HEIGHT   : natural := 4096; 
   116:       -- Cushion between oReady deassertion and BUFFER_BYTES. Covers two 
   117:       -- in-flight cycles of byte_stuffer emission (decision + transfer pipeline) 
   118:       -- plus the +2 EOI footer push. Worst case: 2*BYTES_IN + 2. 
   119:       STALL_MARGIN_BYTES : natural := 2 * math_ceil_div(CO_BYTE_STUFFER_OUT_WIDTH, 8) + 2 
   120:     ); 
   121:     port ( 
   122:       iClk               : in    std_logic; 
   123:       iRst               : in    std_logic; 
   124:       -- Image control 
   125:       iStart             : in    std_logic; 
   126:       iImageWidth        : in    unsigned(log2ceil(MAX_IMAGE_WIDTH + 1) - 1 downto 0); 
   127:       iImageHeight       : in    unsigned(log2ceil(MAX_IMAGE_HEIGHT + 1) - 1 downto 0); 
   128:       iEoi               : in    std_logic; 
   129:       -- Byte stuffer interface 
   130:       iWord              : in    std_logic_vector(IN_WIDTH - 1 downto 0); 
   131:       iValid             : in    std_logic; 
   132:       iByteEnable        : in    unsigned(log2ceil(IN_WIDTH / 8 + 1) - 1 downto 0); 
   133:       oReady             : out   std_logic; 
   134:       -- Output AXI stream interface 
   135:       oWord              : out   std_logic_vector(OUT_WIDTH - 1 downto 0); 
   136:       oValid             : out   std_logic; 
   137:       oByteEnable        : out   unsigned(log2ceil(OUT_WIDTH / 8 + 1) - 1 downto 0); 
   138:       oLast              : out   std_logic; 
   139:       iReady             : in    std_logic 
   140:     ); 
   141:   end entity jls_framer; 
   142:    
   143:   architecture behavioral of jls_framer is 
   144:    
   145:     constant NEAR                 : natural := 0; 
   146:     constant BYTES_IN             : natural := IN_WIDTH / 8; 
   147:     constant BYTES_OUT            : natural := OUT_WIDTH / 8; 
   148:     constant HEADER_LEN           : natural := 25; 
   149:    
   150:     -- Auto-derived FIFO depth (worst-case occupancy; see file header). 
   151:     -- BUFFER_BYTES_NOMINAL handles steady-state (iReady='1') traffic. 
   152:     -- STALL_MARGIN_BYTES extends the FIFO so oAlmostFull can assert at the 
   153:     -- nominal threshold and still absorb in-flight bytes from the upstream 
   154:     -- pipeline (Reg5b → bit_packer → byte_stuffer) before the stall reaches. 
   155:     constant V_IEOI               : natural := BYTES_OUT + BYTES_IN + 1; 
   156:     constant D_DRAIN              : natural := math_ceil_div(V_IEOI, BYTES_OUT); 
   157:     constant N_H                  : natural := math_ceil_div(HEADER_LEN, BYTES_OUT); 
   158:     -- Header-tail carry: bytes of the final header beat not covered by the 
   159:     -- header itself. Expressed via math_min to stay in natural for wide 
   160:     -- OUT_WIDTHs, where N_H * BYTES_OUT - HEADER_LEN exceeds the fill. 
   161:     constant H_SLACK              : natural := N_H * BYTES_OUT - HEADER_LEN; 
   162:     constant BUFFER_BYTES_NOMINAL : natural := math_max( 
   163:                                                         math_max(V_IEOI, D_DRAIN * BYTES_IN), 
   164:                                                         (D_DRAIN + N_H) * BYTES_IN - math_min(BYTES_IN, H_SLACK)); 
   165:     constant BUFFER_BYTES         : natural := BUFFER_BYTES_NOMINAL + STALL_MARGIN_BYTES; 
   166:     constant BUFFER_WIDTH         : natural := BUFFER_BYTES * 8; 
   167:    
   168:     type fsm_t is (idle, header, data); 
   169:    
   170:     signal sFsmState              : fsm_t; 
   171:    
   172:     -- EoI tracker: queue of footer (D9) byte offsets for in-flight images. 
   173:     -- Needed for really small images, like the 4x4 image on T.87 H.3 example 
   174:     constant EOI_FIFO_DEPTH       : natural := 2; 
   175:    
   176:     type eoi_offsets_t is array(natural range <>) of natural range 0 to BUFFER_BYTES; 
   177:    
   178:     signal sBuffer                : std_logic_vector(BUFFER_WIDTH - 1 downto 0); 
   179:     signal sFifoByteCount         : natural range 0 to BUFFER_BYTES; 
   180:     signal sEoiFifo               : eoi_offsets_t(0 to EOI_FIFO_DEPTH - 1); 
   181:     signal sEoiCount              : natural range 0 to EOI_FIFO_DEPTH; 
   182:    
   183:     signal sOutWord               : std_logic_vector(OUT_WIDTH - 1 downto 0); 
   184:     signal sOutValid              : std_logic; 
   185:     signal sOutLast               : std_logic; 
   186:     signal sValidBytes            : unsigned(log2ceil(OUT_WIDTH / 8 + 1) - 1 downto 0); 
   187:    
   188:     signal sHeaderByteIdx         : natural range 0 to HEADER_LEN; 
   189:     -- Pending-start counter. Holds iStart pulses that arrive while an earlier 
   190:     -- image is still being framed. Each EOI dequeues one. Sized to absorb the 
   191:     -- worst-case lead of upstream over the framer when byte_stuffer's FIFO is 
   192:     -- buffering several images at once. 
   193:     constant START_QUEUE_DEPTH    : natural := 4; 
   194:     signal   sNextPending         : natural range 0 to START_QUEUE_DEPTH; 
   195:    
   196:     signal sAxiHandshake          : boolean; 
   197:    
   198:     -- Returns the i-th byte of the 25-byte JPEG-LS frame header. 
   199:     -- Header layout: 
   200:     --   [0-1]   FF D8              SOI 
   201:     --   [2-5]   FF F7 00 0B        SOF55 marker + Lf=11 
   202:     --   [6]     P                  precision (BITNESS) 
   203:     --   [7-8]   Y[15:8] Y[7:0]     image height (runtime) 
   204:     --   [9-10]  X[15:8] X[7:0]     image width  (runtime) 
   205:     --   [11]    01                 Nf=1 
   206:     --   [12-14] 01 11 00           C1=1, H1V1=0x11, Tq1=0 
   207:     --   [15-16] FF DA              SOS marker 
   208:     --   [17-18] 00 08              Ls=8 
   209:     --   [19]    01                 Ns=1 
   210:     --   [20-21] 01 00              Cs1=1, Tm1=0 
   211:     --   [22]    NEAR 
   212:     --   [23-24] 00 00              ILV=0, Al/Ah=0 
   213:    
   214:     function get_header_byte ( 
   215:       idx    : natural; 
   216:       width  : unsigned(15 downto 0); 
   217:       height : unsigned(15 downto 0) 
   218:     ) return std_logic_vector is 
   219:     begin 
   220:    
   221:       case idx is 
   222:    
   223:         when 0 => 
   224:    
   225:           return x"FF"; 
   226:    
   227:         when 1 => 
   228:    
   229:           return x"D8"; 
   230:    
   231:         when 2 => 
   232:    
   233:           return x"FF"; 
   234:    
   235:         when 3 => 
   236:    
   237:           return x"F7"; 
   238:    
   239:         when 4 => 
   240:    
   241:           return x"00"; 
   242:    
   243:         when 5 => 
   244:    
   245:           return x"0B"; 
   246:    
   247:         when 6 => 
   248:    
   249:           return std_logic_vector(to_unsigned(BITNESS, 8)); 
   250:    
   251:         when 7 => 
   252:    
   253:           return std_logic_vector(height(15 downto 8)); 
   254:    
   255:         when 8 => 
   256:    
   257:           return std_logic_vector(height(7 downto 0)); 
   258:    
   259:         when 9 => 
   260:    
   261:           return std_logic_vector(width(15 downto 8)); 
   262:    
   263:         when 10 => 
   264:    
   265:           return std_logic_vector(width(7 downto 0)); 
   266:    
   267:         when 11 => 
   268:    
   269:           return x"01"; 
   270:    
   271:         when 12 => 
   272:    
   273:           return x"01"; 
   274:    
   275:         when 13 => 
   276:    
   277:           return x"11"; 
   278:    
   279:         when 14 => 
   280:    
   281:           return x"00"; 
   282:    
   283:         when 15 => 
   284:    
   285:           return x"FF"; 
   286:    
   287:         when 16 => 
   288:    
   289:           return x"DA"; 
   290:    
   291:         when 17 => 
   292:    
   293:           return x"00"; 
   294:    
   295:         when 18 => 
   296:    
   297:           return x"08"; 
   298:    
   299:         when 19 => 
   300:    
   301:           return x"01"; 
   302:    
   303:         when 20 => 
   304:    
   305:           return x"01"; 
   306:    
   307:         when 21 => 
   308:    
   309:           return x"00"; 
   310:    
   311:         when 22 => 
   312:    
   313:           return std_logic_vector(to_unsigned(NEAR, 8)); 
   314:    
   315:         when 23 => 
   316:    
   317:           return x"00"; 
   318:    
   319:         when 24 => 
   320:    
   321:           return x"00"; 
   322:    
   323:         when others => 
   324:    
   325:           return x"00"; 
   326:    
   327:       end case; 
   328:    
   329:     end function get_header_byte; 
   330:    
   331:     -- Pop head EoI: shift remaining entries down, decrement their offsets by 
   332:     -- bytes popped this beat, decrement count. 
   333:    
   334:     procedure pop_eoi ( 
   335:       variable fifo : inout eoi_offsets_t; 
   336:       variable cnt  : inout natural; 
   337:       constant emit : in natural 
   338:     ) is 
   339:     begin 
   340:    
   341:       for k in 0 to EOI_FIFO_DEPTH - 2 loop 
   342:    
   343:         if (k + 1 < cnt) then 
   344:           fifo(k) := fifo(k + 1) - emit; 
   345:         else 
   346:           fifo(k) := 0; 
   347:         end if; 
   348:    
   349:       end loop; 
   350:    
   351:       fifo(EOI_FIFO_DEPTH - 1) := 0; 
   352:       cnt                      := cnt - 1; 
   353:    
   354:     end procedure pop_eoi; 
   355:    
   356:     -- Decrement all live EoI offsets by bytes popped this beat (no head pop). 
   357:    
   358:     procedure dec_eoi ( 
   359:       variable fifo : inout eoi_offsets_t; 
   360:       constant cnt  : in natural; 
   361:       constant emit : in natural 
   362:     ) is 
   363:     begin 
   364:    
   365:       for k in 0 to EOI_FIFO_DEPTH - 1 loop 
   366:    
   367:         if (k < cnt) then 
   368:           fifo(k) := fifo(k) - emit; 
   369:         end if; 
   370:    
   371:       end loop; 
   372:    
   373:     end procedure dec_eoi; 
   374:    
   375:   begin 
   376:    
   377:     assert IN_WIDTH mod 8 = 0 
   378:       report "jls_framer: IN_WIDTH must be a multiple of 8" 
   379:       severity failure; 
   380:    
   381:     assert OUT_WIDTH mod 8 = 0 
   382:       report "jls_framer: OUT_WIDTH must be a multiple of 8" 
   383:       severity failure; 
   384:    
   385:     assert BYTES_OUT >= BYTES_IN + 1 
   386:       report "jls_framer: OUT_WIDTH must be at least one byte wider than IN_WIDTH for FIFO stability under absolute worst-case scenario (sustained max-rate output across back-to-back images)" 
   387:       severity failure; 
   388:    
   389:     sAxiHandshake <= (iReady and sOutValid) = '1'; 
   390:    
   391:     -- Contract assertions in PSL: the output side is an AXI-Stream master — 
   392:     -- these hold for any downstream behaviour (active in NVC sims via --psl, 
   393:     -- plain comments to synthesis). 
   394:     -- psl default clock is rising_edge(iClk); 
   395:     -- psl assert always (iRst = '1' -> next (oValid = '0' and oLast = '0')) report "jls_framer: reset must clear the output stream"; 
   396:     -- psl assert always ((iRst = '0' and oValid = '1' and iReady = '0') -> next (oValid = '1')) report "jls_framer: oValid must hold until the beat is accepted (AXIS)"; 
   397:     -- psl assert never (oLast = '1' and oValid = '0') report "jls_framer: oLast is only meaningful on a valid beat"; 
   398:    
   399:     oWord       <= sOutWord; 
   400:     oValid      <= sOutValid; 
   401:     oByteEnable <= sValidBytes; 
   402:     oLast       <= sOutLast; 
   403:    
   404:     -- Ready/valid handshake with byte_stuffer: deasserts at the nominal 
   405:     -- sizing threshold, leaving STALL_MARGIN_BYTES headroom for the 
   406:     -- 1-cycle handshake latency (byte_stuffer's registered output). 
   407:     oReady <= '1' when sFifoByteCount < BUFFER_BYTES_NOMINAL else 
   408:               '0'; 
   409:    
   410:     ------------------------------------------------------------------------------------------------------------- 
   411:     -- SYNCHRONOUS PROCESS 
   412:     ------------------------------------------------------------------------------------------------------------- 
   413:     sync_proc : process (iClk) is 
   414:    
   415:       variable vBuffer            : std_logic_vector(BUFFER_WIDTH - 1 downto 0); 
   416:       variable vFifoByteCount     : natural range 0 to BUFFER_BYTES; 
   417:       variable vEoiIdxFifo        : eoi_offsets_t(0 to EOI_FIFO_DEPTH - 1); 
   418:       variable vEoiCount          : natural range 0 to EOI_FIFO_DEPTH; 
   419:       variable vWidth             : unsigned(15 downto 0); 
   420:       variable vHeight            : unsigned(15 downto 0); 
   421:       variable vHeaderBytesRemain : natural range 0 to HEADER_LEN; 
   422:       variable vDataNeededBytes   : natural range 0 to BYTES_OUT; 
   423:       variable vEmitDataBytes     : natural range 0 to BYTES_OUT; 
   424:       variable vCanEmit           : boolean; 
   425:       variable vEoiInBeat         : boolean; 
   426:       variable vOffsetI           : natural range 0 to BYTES_OUT; 
   427:       variable vNextPending       : natural range 0 to START_QUEUE_DEPTH; 
   428:    
   429:     begin 
   430:    
   431:       if rising_edge(iClk) then 
   432:         if (iRst = '1') then 
   433:           sFsmState      <= idle; 
   434:           sBuffer        <= (others => '0'); 
   435:           sFifoByteCount <= 0; 
   436:           sEoiFifo       <= (others => 0); 
   437:           sEoiCount      <= 0; 
   438:           sOutWord       <= (others => '0'); 
   439:           sOutValid      <= '0'; 
   440:           sOutLast       <= '0'; 
   441:           sValidBytes    <= (others => '0'); 
   442:           sHeaderByteIdx <= 0; 
   443:           sNextPending   <= 0; 
   444:         else 
   445:           vBuffer        := sBuffer; 
   446:           vFifoByteCount := sFifoByteCount; 
   447:           vEoiIdxFifo    := sEoiFifo; 
   448:           vEoiCount      := sEoiCount; 
   449:           vWidth         := resize(iImageWidth, 16); 
   450:           vHeight        := resize(iImageHeight, 16); 
   451:           vCanEmit       := sAxiHandshake or sOutValid = '0'; 
   452:           vNextPending   := sNextPending; 
   453:    
   454:           -- iStart latching: queue starts so back-to-back images can be buffered 
   455:           -- in byte_stuffer while framer drains earlier images. 
   456:           if (iStart = '1') then 
   457:             assert vNextPending < START_QUEUE_DEPTH 
   458:               report "jls_framer: iStart dropped (start queue full)" 
   459:               severity failure; 
   460:             vNextPending := vNextPending + 1; 
   461:           end if; 
   462:    
   463:           ----------------------------------------------------------------------------------------------------- 
   464:           -- READ 
   465:           ----------------------------------------------------------------------------------------------------- 
   466:           -- Beat was consumed 
   467:           if (sAxiHandshake) then 
   468:             sOutValid <= '0'; 
   469:             sOutLast  <= '0'; 
   470:           end if; 
   471:    
   472:           case sFsmState is 
   473:    
   474:             when idle => 
   475:    
   476:               if (vNextPending > 0) then 
   477:                 sFsmState      <= header; 
   478:                 sHeaderByteIdx <= 0; 
   479:                 vNextPending   := vNextPending - 1; 
   480:               end if; 
   481:    
   482:             when header => 
   483:    
   484:               if (vCanEmit) then 
   485:                 vHeaderBytesRemain := HEADER_LEN - sHeaderByteIdx; 
   486:    
   487:                 if (vHeaderBytesRemain >= BYTES_OUT) then 
   488:                   -- Full header beat from HEADER ROM 
   489:    
   490:                   for i in 0 to BYTES_OUT - 1 loop 
   491:    
   492:                     sOutWord(OUT_WIDTH - 1 - i * 8 downto OUT_WIDTH - (i + 1) * 8) <= get_header_byte(sHeaderByteIdx + i, vWidth, vHeight); 
   493:    
   494:                   end loop; 
   495:    
   496:                   sValidBytes <= to_unsigned(BYTES_OUT, sValidBytes'length); 
   497:                   sOutValid   <= '1'; 
   498:                   sOutLast    <= '0'; 
   499:    
   500:                   if (sHeaderByteIdx + BYTES_OUT = HEADER_LEN) then 
   501:                     sFsmState <= data; 
   502:                   else 
   503:                     sHeaderByteIdx <= sHeaderByteIdx + BYTES_OUT; 
   504:                   end if; 
   505:                 else 
   506:                   -- Last partial beat, fill remaining bytes from the DATA FIFO 
   507:    
   508:                   vDataNeededBytes := BYTES_OUT - vHeaderBytesRemain; 
   509:    
   510:                   -- Check for EOI in the FIFO bytes 
   511:                   vEoiInBeat := vEoiCount > 0 and vEoiIdxFifo(0) < vDataNeededBytes; 
   512:                   if (vEoiInBeat) then 
   513:                     vEmitDataBytes := vEoiIdxFifo(0) + 1; 
   514:                   else 
   515:                     vEmitDataBytes := vDataNeededBytes; 
   516:                   end if; 
   517:    
   518:                   if (vFifoByteCount >= vEmitDataBytes) then 
   519:                     -- FIFO has enough bytes 
   520:                     -- Otherwise it'll stall here until it does 
   521:    
   522:                     for i in 0 to BYTES_OUT - 1 loop 
   523:    
   524:                       if (i < vHeaderBytesRemain) then 
   525:                         sOutWord(OUT_WIDTH - 1 - i * 8 downto OUT_WIDTH - (i + 1) * 8) <= get_header_byte(sHeaderByteIdx + i, vWidth, vHeight); 
   526:                       elsif (i < vHeaderBytesRemain + vEmitDataBytes) then 
   527:                         vOffsetI                                                       := i - vHeaderBytesRemain; 
   528:                         sOutWord(OUT_WIDTH - 1 - i * 8 downto OUT_WIDTH - (i + 1) * 8) <= vBuffer(BUFFER_WIDTH - 1 - vOffsetI * 8 downto BUFFER_WIDTH - (vOffsetI + 1) * 8); 
   529:                       else 
   530:                         sOutWord(OUT_WIDTH - 1 - i * 8 downto OUT_WIDTH - (i + 1) * 8) <= (others => '0'); 
   531:                       end if; 
   532:    
   533:                     end loop; 
   534:    
   535:                     sValidBytes    <= to_unsigned(vHeaderBytesRemain + vEmitDataBytes, sValidBytes'length); 
   536:                     sOutValid      <= '1'; 
   537:                     vBuffer        := std_logic_vector(shift_left(unsigned(vBuffer), vEmitDataBytes * 8)); 
   538:                     vFifoByteCount := vFifoByteCount - vEmitDataBytes; 
   539:    
   540:                     if (vEoiInBeat) then 
   541:                       sOutLast <= '1'; 
   542:                       pop_eoi(vEoiIdxFifo, vEoiCount, vEmitDataBytes); 
   543:    
   544:                       if (vNextPending > 0) then 
   545:                         sFsmState      <= header; 
   546:                         sHeaderByteIdx <= 0; 
   547:                         vNextPending   := vNextPending - 1; 
   548:                       else 
   549:                         sFsmState <= idle; 
   550:                       end if; 
   551:                     else 
   552:                       sOutLast  <= '0'; 
   553:                       dec_eoi(vEoiIdxFifo, vEoiCount, vEmitDataBytes); 
   554:                       sFsmState <= data; 
   555:                     end if; 
   556:                   end if; 
   557:                 end if; 
   558:               end if; 
   559:    
   560:             when data => 
   561:    
   562:               if (vCanEmit) then 
   563:                 vEoiInBeat := vEoiCount > 0 and vEoiIdxFifo(0) < BYTES_OUT; 
   564:                 if (vEoiInBeat) then 
   565:                   vEmitDataBytes := vEoiIdxFifo(0) + 1; 
   566:                 else 
   567:                   vEmitDataBytes := BYTES_OUT; 
   568:                 end if; 
   569:    
   570:                 if (vFifoByteCount >= vEmitDataBytes) then 
   571:    
   572:                   for i in 0 to BYTES_OUT - 1 loop 
   573:    
   574:                     if (i < vEmitDataBytes) then 
   575:                       sOutWord(OUT_WIDTH - 1 - i * 8 downto OUT_WIDTH - (i + 1) * 8) <= vBuffer(BUFFER_WIDTH - 1 - i * 8 downto BUFFER_WIDTH - (i + 1) * 8); 
   576:                     else 
   577:                       sOutWord(OUT_WIDTH - 1 - i * 8 downto OUT_WIDTH - (i + 1) * 8) <= (others => '0'); 
   578:                     end if; 
   579:    
   580:                   end loop; 
   581:    
   582:                   sValidBytes    <= to_unsigned(vEmitDataBytes, sValidBytes'length); 
   583:                   sOutValid      <= '1'; 
   584:                   vBuffer        := std_logic_vector(shift_left(unsigned(vBuffer), vEmitDataBytes * 8)); 
   585:                   vFifoByteCount := vFifoByteCount - vEmitDataBytes; 
   586:    
   587:                   if (vEoiInBeat) then 
   588:                     sOutLast <= '1'; 
   589:                     pop_eoi(vEoiIdxFifo, vEoiCount, vEmitDataBytes); 
   590:    
   591:                     if (vNextPending > 0) then 
   592:                       sFsmState      <= header; 
   593:                       sHeaderByteIdx <= 0; 
   594:                       vNextPending   := vNextPending - 1; 
   595:                     else 
   596:                       sFsmState <= idle; 
   597:                     end if; 
   598:                   else 
   599:                     sOutLast <= '0'; 
   600:                     dec_eoi(vEoiIdxFifo, vEoiCount, vEmitDataBytes); 
   601:                   end if; 
   602:                 end if; 
   603:               end if; 
   604:    
   605:           end case; 
   606:    
   607:           ----------------------------------------------------------------------------------------------------- 
   608:           -- WRITE 
   609:           ----------------------------------------------------------------------------------------------------- 
   610:           if (iValid = '1') then 
   611:    
   612:             for i in 0 to BYTES_IN - 1 loop 
   613:    
   614:               if (i < to_integer(iByteEnable)) then 
   615:                 vBuffer(BUFFER_WIDTH - 1 - (vFifoByteCount + i) * 8 downto BUFFER_WIDTH - (vFifoByteCount + i + 1) * 8) := iWord(IN_WIDTH - 1 - i * 8 downto IN_WIDTH - (i + 1) * 8); 
   616:               end if; 
   617:    
   618:             end loop; 
   619:    
   620:             vFifoByteCount := vFifoByteCount + to_integer(iByteEnable); 
   621:    
   622:             if (iEoi = '1') then 
   623:               -- Push FOOTER into the FIFO right after the last data byte 
   624:               vBuffer(BUFFER_WIDTH - 1 - vFifoByteCount * 8 downto BUFFER_WIDTH - (vFifoByteCount + 1) * 8)       := x"FF"; 
   625:               vBuffer(BUFFER_WIDTH - 1 - (vFifoByteCount + 1) * 8 downto BUFFER_WIDTH - (vFifoByteCount + 2) * 8) := x"D9"; 
   626:               vFifoByteCount                                                                                      := vFifoByteCount + 2; 
   627:    
   628:               assert vEoiCount < EOI_FIFO_DEPTH 
   629:                 report "jls_framer: EoI FIFO overflow; back-to-back images closer than depth allows" 
   630:                 severity failure; 
   631:    
   632:               vEoiIdxFifo(vEoiCount) := vFifoByteCount - 1; 
   633:               vEoiCount              := vEoiCount + 1; 
   634:             end if; 
   635:           end if; 
   636:    
   637:           assert vFifoByteCount <= BUFFER_BYTES 
   638:             report "jls_framer: payload FIFO overflow (vCount exceeds BUFFER_BYTES; check sizing assumptions or sustained backpressure)" 
   639:             severity failure; 
   640:    
   641:           sBuffer        <= vBuffer; 
   642:           sFifoByteCount <= vFifoByteCount; 
   643:           sEoiFifo       <= vEoiIdxFifo; 
   644:           sEoiCount      <= vEoiCount; 
   645:           sNextPending   <= vNextPending; 
   646:         end if; 
   647:       end if; 
   648:    
   649:     end process sync_proc; 
   650:    
   651:   end architecture behavioral;