From 7c56def929d55f8dc32d39f14d70ea65b7c587e8 Mon Sep 17 00:00:00 2001 From: zhouhua Date: Thu, 16 Apr 2026 19:40:36 +0800 Subject: [PATCH] Implement discrete dynamic NEAR ladder strategy --- docs/jls_module_interfaces.md | 8 +- docs/jls_pipeline_mermaid.md | 4 +- fpga/sim/tb_jls_near_ctrl.sv | 12 +- fpga/srs/jpeg_ls.md | 28 ++- fpga/verilog/jls_near_ctrl.sv | 338 ++++++++++++++++++++++++++++++---- 5 files changed, 334 insertions(+), 56 deletions(-) diff --git a/docs/jls_module_interfaces.md b/docs/jls_module_interfaces.md index b673fb3..cc4c306 100644 --- a/docs/jls_module_interfaces.md +++ b/docs/jls_module_interfaces.md @@ -189,7 +189,13 @@ Responsibilities: - Keep NEAR at 0 for `ratio=0` and invalid ratio values. - For `ratio=1/2/3`, update the next strip's NEAR after the current strip output byte count is known. -- Clamp NEAR to `0..31`. +- Use the discrete NEAR ladder `{0, 1, 2, 4, 8, 16, 31}` instead of linear + `+1/-1` arithmetic. +- After strip 0, choose a coarse NEAR rung from the first strip's + actual-vs-target ratio bucket. +- For later strips, apply cumulative-bit micro-adjustments of `+2`, `+1`, `0`, + `-1`, or `-2` rungs using shift-add threshold bands. +- Clamp the resulting rung to the supported ladder endpoints. - Report a sticky target-miss condition when the cumulative actual bits still exceed cumulative target bits while NEAR is already 31. diff --git a/docs/jls_pipeline_mermaid.md b/docs/jls_pipeline_mermaid.md index 46167ff..db77308 100644 --- a/docs/jls_pipeline_mermaid.md +++ b/docs/jls_pipeline_mermaid.md @@ -21,7 +21,7 @@ flowchart TB SCAN["S01 jls_scan_ctrl\nIn: pixel event, current_near\nDo: start/finish one standalone strip frame, choose first-strip NEAR=0\nOut: enc pixel event, strip_start/finish, strip width/height/near\nStd: Annex A.8, Annex D.1-D.3"] PRESET["S02 jls_preset_defaults + jls_coding_params\nIn: PIX_WIDTH, strip NEAR\nDo: default MAXVAL/T1/T2/T3/RESET, RANGE/qbpp/LIMIT lookup\nOut: header preset fields, active coding params\nStd: Annex A.2, Annex C.2.4.1.1, Annex G.2"] HDR["S03 jls_header_writer\nIn: strip_start/finish, strip size, NEAR, preset params\nDo: emit SOI/SOF55/LSE/SOS and EOI, big-endian marker fields\nOut: header/eoi byte stream, original_image_start sideband\nStd: Annex C.1-C.4, Annex D.3"] - NEAR["S04 jls_near_ctrl\nIn: image_start, strip_done, strip pixels, output bytes, ratio\nDo: cumulative actual-vs-target NEAR update and clamp 0..31\nOut: current_near, target_miss_at_max_near\nStd: NEAR usage in Annex A/C/D; dynamic policy is project-specific"] + NEAR["S04 jls_near_ctrl\nIn: image_start, strip_done, strip pixels, output bytes, ratio\nDo: first-strip ratio bucket jump to NEAR ladder {0,1,2,4,8,16,31}; later strips use cumulative actual-vs-target +/-1 or +/-2 rung updates\nOut: current_near, target_miss_at_max_near\nStd: NEAR usage in Annex A/C/D; dynamic policy is project-specific"] end subgraph PIX["Pixel neighborhood and mode decision"] @@ -87,7 +87,7 @@ flowchart TB | S01 | `jls_scan_ctrl` | Pixel event, `current_near`, downstream readiness. | Split the original image into standalone strip frames, emit strip start/finish commands, force the first strip NEAR to 0. | Encode pixel event, strip start/finish event, `strip_width`, `strip_height`, `strip_near`, strip pixel count. | Annex A.8; Annex D.1-D.3 scan control. | | S02 | `jls_preset_defaults`, `jls_coding_params` | `PIX_WIDTH`, strip `NEAR`. | Compute default `MAXVAL/T1/T2/T3/RESET`; lookup `RANGE/qbpp/LIMIT` for the active strip. | Preset fields for LSE/header and active coding parameters for regular/run mode. | Annex A.2; Annex C.2.4.1.1 preset parameters; Annex G.2 coding parameters. | | S03 | `jls_header_writer` | Strip start/finish, strip size, `NEAR`, preset fields. | Emit `SOI/SOF55/LSE/SOS` at strip start and `EOI` after payload flush. | Header/EOI byte stream and `original_image_start` sideband. | Annex C.1-C.4 marker syntax; Annex D.3 scan syntax. | -| S04 | `jls_near_ctrl` | Image start ratio, strip output byte count, strip pixel count. | Project dynamic policy: cumulative actual-vs-target bits, step `NEAR` up/down, clamp to `0..31`. | `current_near`, cumulative bit counters, target-miss flag. | Standard NEAR usage is Annex A/C/D; dynamic ratio policy is project-specific. | +| S04 | `jls_near_ctrl` | Image start ratio, strip output byte count, strip pixel count. | Project dynamic policy: use strip 0 actual-vs-target ratio bucket to jump onto the discrete NEAR ladder `{0,1,2,4,8,16,31}`, then use cumulative actual-vs-target shift-add bands to move `NEAR` by `+2`, `+1`, `0`, `-1`, or `-2` ladder steps. | `current_near`, cumulative bit counters, target-miss flag. | Standard NEAR usage is Annex A/C/D; dynamic ratio policy is project-specific. | | S10 | `jls_neighbor_provider` | Encode pixel event, reconstructed writeback `Rx`, active strip width, `NEAR`. | Maintain reconstructed line history in two banks, apply top/left/right edge rules, produce JPEG-LS neighbors. For `NEAR=0`, commit `X` as `Rx` immediately. For `NEAR>0`, a non-EOL writeback can overlap the next same-row pixel accept by bypassing returned `Rx` as that pixel's `Ra`; row transitions still wait one clock. Regular-mode true `Rx` returns immediately after S24 accepts the `Errval/Rx` result rather than after Golomb completion. | Neighbor event with `X`, `x/y`, `Ra/Rb/Rc/Rd`, strip flags. | Annex A.3 local gradients; Annex A.4 prediction neighborhood. | | S11 | `jls_mode_router` | Neighbor event, `strip_width`, `NEAR`. | Determine regular/run entry from gradients, then stay in the Annex A.7 run loop while `run_length_accum` is non-zero; accumulate matching run pixels, reconstruct them as `Ra`, and form run segments at EOL/interruption. Later non-EOL matching run pixels may overlap an outstanding run segment because they emit no entropy yet. | Regular event or run segment; direct run-pixel reconstruction. | Annex A.3 context determination; Annex A.7 run mode. | | S20 | `jls_predictor` | Regular event `X,Ra,Rb,Rc,Rd`. | Compute MED prediction `Px`. | Predicted event with `Px` and neighbor metadata. | Annex A.4 MED predictor pseudocode. | diff --git a/fpga/sim/tb_jls_near_ctrl.sv b/fpga/sim/tb_jls_near_ctrl.sv index a7fcb6e..02b64eb 100644 --- a/fpga/sim/tb_jls_near_ctrl.sv +++ b/fpga/sim/tb_jls_near_ctrl.sv @@ -84,7 +84,7 @@ module tb_jls_near_ctrl; strip_done_valid = 1'b0; @(posedge clk); #1; - if (current_near !== 6'd1 || actual_bits_cumulative !== 48'd800 || + if (current_near !== 6'd4 || actual_bits_cumulative !== 48'd800 || target_bits_cumulative !== 48'd512) begin $fatal(1, "ratio=2 first strip update mismatch"); end @@ -96,7 +96,7 @@ module tb_jls_near_ctrl; strip_done_valid = 1'b0; @(posedge clk); #1; - if (current_near !== 6'd2 || actual_bits_cumulative !== 48'd1120 || + if (current_near !== 6'd8 || actual_bits_cumulative !== 48'd1120 || target_bits_cumulative !== 48'd1024) begin $fatal(1, "ratio=2 second strip update mismatch"); end @@ -108,7 +108,7 @@ module tb_jls_near_ctrl; strip_done_valid = 1'b0; @(posedge clk); #1; - if (current_near !== 6'd1 || actual_bits_cumulative !== 48'd1200 || + if (current_near !== 6'd4 || actual_bits_cumulative !== 48'd1200 || target_bits_cumulative !== 48'd1536) begin $fatal(1, "ratio=2 third strip update mismatch"); end @@ -131,15 +131,15 @@ module tb_jls_near_ctrl; $fatal(1, "lossless ratio should force NEAR to 0 without target miss"); end - // Repeated over-target strips saturate at MAX_NEAR and then set the sticky - // miss flag on the next over-target completion. + // Repeated over-target strips jump up the discrete NEAR ladder and then + // set the sticky miss flag once the ladder is already saturated. @(posedge clk); image_ratio = 4'd1; image_start_valid = 1'b1; @(posedge clk); image_start_valid = 1'b0; - for (loop_index = 0; loop_index < 32; loop_index = loop_index + 1) begin + for (loop_index = 0; loop_index < 8; loop_index = loop_index + 1) begin @(posedge clk); strip_output_bytes = 32'd1000; strip_done_valid = 1'b1; diff --git a/fpga/srs/jpeg_ls.md b/fpga/srs/jpeg_ls.md index 5b842d8..b987049 100644 --- a/fpga/srs/jpeg_ls.md +++ b/fpga/srs/jpeg_ls.md @@ -230,14 +230,26 @@ SOF 协议: - `ratio=0` 或非法 ratio 时,整帧 `NEAR` 固定为 0,不进行动态调整。 - `ratio=1/2/3` 时,每个条带 frame 结束后比较累计实际输出 bit 数与累计目标 bit 数,并调整下一条带 frame 的 `NEAR`。 -- 第一版调节策略: - - 若累计实际输出 bit 数大于累计目标 bit 数,`NEAR = NEAR + 1`。 - - 若累计实际输出 bit 数小于累计目标 bit 数且 `NEAR > 0`, - `NEAR = NEAR - 1`。 - - 若二者相等,`NEAR` 保持不变。 - - `NEAR` 钳位到 `0..31`。 -- 如果第一版效果不理想,再参考 CN102088602A 所述方法优化调节步长和累计偏差 - 控制策略。 +- 当前实现采用离散 `NEAR` 档位集合 + `{0, 1, 2, 4, 8, 16, 31}`,而不是逐条带线性 `+1/-1`。 +- 第一条带结束后,控制器根据首条带 `actual_bits / target_bits` 的粗分档直接跳到 + 一个离散 `NEAR` 档位;当前 RTL 为便于时序收敛,采用移位加法近似阈值: + - `<= 1.125x target` -> `NEAR=0` + - `<= 1.25x target` -> `NEAR=1` + - `<= 1.5x target` -> `NEAR=2` + - `<= 2x target` -> `NEAR=4` + - `<= 3x target` -> `NEAR=8` + - `<= 5x target` -> `NEAR=16` + - `> 5x target` -> `NEAR=31` +- 第二条带及后续条带使用累计 bit 偏差做离散档位微调,而不是重新全量估计: + - 若累计实际 bit 数 `> target + target/4`,档位 `+2` + - 若累计实际 bit 数 `> target + target/16`,档位 `+1` + - 若累计实际 bit 数 `< target - target/4`,档位 `-2` + - 若累计实际 bit 数 `< target - target/16`,档位 `-1` + - 否则保持当前档位不变 +- 档位更新后仍按集合 `{0,1,2,4,8,16,31}` 钳位,不输出非整数 `NEAR`。 +- 若 `NEAR` 已处于 `31` 且累计实际 bit 数仍高于累计目标 bit 数,置 + `target_miss_at_max_near` 粘滞标志,供验证报告使用。 ### 4.3 误差验收 diff --git a/fpga/verilog/jls_near_ctrl.sv b/fpga/verilog/jls_near_ctrl.sv index a54bd21..6704192 100644 --- a/fpga/verilog/jls_near_ctrl.sv +++ b/fpga/verilog/jls_near_ctrl.sv @@ -7,8 +7,9 @@ // Example : For ratio=2, target bits are source bits divided by 4. // // Dynamic NEAR controller. This project-specific controller keeps NEAR at 0 -// for lossless/invalid ratios and applies a simple cumulative actual-vs-target -// step after each standalone strip frame is fully output. +// for lossless/invalid ratios, uses a first-strip coarse jump to a discrete +// NEAR level, then applies cumulative actual-vs-target micro-adjustments on the +// remaining strip frames. `default_nettype none @@ -63,12 +64,22 @@ module jls_near_ctrl #( localparam logic [3:0] RATIO_1_TO_4 = 4'd2; localparam logic [3:0] RATIO_1_TO_8 = 4'd3; - // Saturated project maximum NEAR value. - localparam logic [5:0] MAX_NEAR_VALUE = MAX_NEAR[5:0]; + // Discrete NEAR ladder used by the dynamic controller: + // level 0 -> 0, 1 -> 1, 2 -> 2, 3 -> 4, 4 -> 8, 5 -> 16, 6 -> 31. + localparam logic [2:0] NEAR_LEVEL_0 = 3'd0; + localparam logic [2:0] NEAR_LEVEL_1 = 3'd1; + localparam logic [2:0] NEAR_LEVEL_2 = 3'd2; + localparam logic [2:0] NEAR_LEVEL_4 = 3'd3; + localparam logic [2:0] NEAR_LEVEL_8 = 3'd4; + localparam logic [2:0] NEAR_LEVEL_16 = 3'd5; + localparam logic [2:0] NEAR_LEVEL_31 = 3'd6; // Latched ratio for the current original image. logic [3:0] active_ratio; + // Internal discrete NEAR level register. current_near is decoded from it. + logic [2:0] current_near_level; + // Strip-level source and target bit calculations. logic [47:0] strip_pixel_count_ext; logic [47:0] strip_source_bits; @@ -80,24 +91,86 @@ module jls_near_ctrl #( logic [47:0] target_bits_sum; // Registered strip-completion update. This splits the 48-bit adders from - // the actual-vs-target compare and NEAR step logic for 250 MHz timing. + // the ratio-bucket compare and NEAR-level update logic for 250 MHz timing. logic pending_update_valid; logic [47:0] pending_actual_bits_sum; logic [47:0] pending_target_bits_sum; logic pending_ratio_is_lossless_or_invalid; + logic pending_first_strip_update; - // Ratio classification and NEAR update decisions. + // Ratio classification. logic ratio_is_lossless_or_invalid; - logic actual_over_target; - logic actual_under_target; - logic near_can_increase; - logic near_can_decrease; - logic near_is_max; + + // Shift-add thresholds for the first-strip coarse jump. These are chosen to + // approximate the project review buckets while avoiding wide constant + // multipliers in the 250 MHz strip-control path. + logic [47:0] target_plus_one_eighth; + logic [47:0] target_plus_one_quarter; + logic [47:0] target_plus_one_half; + logic [47:0] target_times_two; + logic [47:0] target_times_three; + logic [47:0] target_times_five; + logic [2:0] first_strip_level_next; + + // Cumulative micro-adjust thresholds for later strips. +/-1/16 is the hold + // band; beyond +/-1/4 the controller skips one NEAR rung. + logic [47:0] target_plus_sixteenth; + logic [47:0] target_minus_sixteenth; + logic [47:0] target_plus_quarter; + logic [47:0] target_minus_quarter; + logic step_up_one_level; + logic step_up_two_levels; + logic step_down_one_level; + logic step_down_two_levels; + + // Discrete NEAR ladder movement. + logic [2:0] near_level_plus_one; + logic [2:0] near_level_plus_two; + logic [2:0] near_level_minus_one; + logic [2:0] near_level_minus_two; + logic [2:0] adjusted_near_level_next; + + // Max-level sticky miss reporting. + logic near_level_is_max; + logic target_still_missed_at_max; always_comb begin update_busy = pending_update_valid; end + always_comb begin + current_near = 6'd0; + case (current_near_level) + NEAR_LEVEL_1: begin + current_near = 6'd1; + end + + NEAR_LEVEL_2: begin + current_near = 6'd2; + end + + NEAR_LEVEL_4: begin + current_near = 6'd4; + end + + NEAR_LEVEL_8: begin + current_near = 6'd8; + end + + NEAR_LEVEL_16: begin + current_near = 6'd16; + end + + NEAR_LEVEL_31: begin + current_near = 6'd31; + end + + default: begin + current_near = 6'd0; + end + endcase + end + always_comb begin strip_pixel_count_ext = {16'd0, strip_pixel_count}; end @@ -168,53 +241,236 @@ module jls_near_ctrl #( always_comb begin ratio_is_lossless_or_invalid = 1'b0; case (active_ratio) - RATIO_1_TO_2: ratio_is_lossless_or_invalid = 1'b0; - RATIO_1_TO_4: ratio_is_lossless_or_invalid = 1'b0; - RATIO_1_TO_8: ratio_is_lossless_or_invalid = 1'b0; - default: ratio_is_lossless_or_invalid = 1'b1; + RATIO_1_TO_2: begin + ratio_is_lossless_or_invalid = 1'b0; + end + + RATIO_1_TO_4: begin + ratio_is_lossless_or_invalid = 1'b0; + end + + RATIO_1_TO_8: begin + ratio_is_lossless_or_invalid = 1'b0; + end + + default: begin + ratio_is_lossless_or_invalid = 1'b1; + end endcase end always_comb begin - actual_over_target = 1'b0; - if (pending_actual_bits_sum > pending_target_bits_sum) begin - actual_over_target = 1'b1; + target_plus_one_eighth = pending_target_bits_sum + {3'b000, pending_target_bits_sum[47:3]}; + target_plus_one_quarter = pending_target_bits_sum + {2'b00, pending_target_bits_sum[47:2]}; + target_plus_one_half = pending_target_bits_sum + {1'b0, pending_target_bits_sum[47:1]}; + target_times_two = {pending_target_bits_sum[46:0], 1'b0}; + target_times_three = pending_target_bits_sum + {pending_target_bits_sum[46:0], 1'b0}; + target_times_five = pending_target_bits_sum + {pending_target_bits_sum[45:0], 2'b00}; + end + + always_comb begin + first_strip_level_next = NEAR_LEVEL_31; + if (pending_actual_bits_sum <= target_plus_one_eighth) begin + first_strip_level_next = NEAR_LEVEL_0; + end else if (pending_actual_bits_sum <= target_plus_one_quarter) begin + first_strip_level_next = NEAR_LEVEL_1; + end else if (pending_actual_bits_sum <= target_plus_one_half) begin + first_strip_level_next = NEAR_LEVEL_2; + end else if (pending_actual_bits_sum <= target_times_two) begin + first_strip_level_next = NEAR_LEVEL_4; + end else if (pending_actual_bits_sum <= target_times_three) begin + first_strip_level_next = NEAR_LEVEL_8; + end else if (pending_actual_bits_sum <= target_times_five) begin + first_strip_level_next = NEAR_LEVEL_16; end end always_comb begin - actual_under_target = 1'b0; - if (pending_actual_bits_sum < pending_target_bits_sum) begin - actual_under_target = 1'b1; + target_plus_sixteenth = pending_target_bits_sum + {4'b0000, pending_target_bits_sum[47:4]}; + target_minus_sixteenth = pending_target_bits_sum - {4'b0000, pending_target_bits_sum[47:4]}; + target_plus_quarter = pending_target_bits_sum + {2'b00, pending_target_bits_sum[47:2]}; + target_minus_quarter = pending_target_bits_sum - {2'b00, pending_target_bits_sum[47:2]}; + end + + always_comb begin + step_up_one_level = 1'b0; + if (pending_actual_bits_sum > target_plus_sixteenth) begin + step_up_one_level = 1'b1; end end always_comb begin - near_is_max = 1'b0; - if (current_near >= MAX_NEAR_VALUE) begin - near_is_max = 1'b1; + step_up_two_levels = 1'b0; + if (pending_actual_bits_sum > target_plus_quarter) begin + step_up_two_levels = 1'b1; end end always_comb begin - near_can_increase = 1'b0; - if (!pending_ratio_is_lossless_or_invalid && actual_over_target && !near_is_max) begin - near_can_increase = 1'b1; + step_down_one_level = 1'b0; + if (pending_actual_bits_sum < target_minus_sixteenth) begin + step_down_one_level = 1'b1; end end always_comb begin - near_can_decrease = 1'b0; - if (!pending_ratio_is_lossless_or_invalid && actual_under_target && - current_near != 6'd0) begin - near_can_decrease = 1'b1; + step_down_two_levels = 1'b0; + if (pending_actual_bits_sum < target_minus_quarter) begin + step_down_two_levels = 1'b1; + end + end + + always_comb begin + near_level_plus_one = current_near_level; + case (current_near_level) + NEAR_LEVEL_0: begin + near_level_plus_one = NEAR_LEVEL_1; + end + + NEAR_LEVEL_1: begin + near_level_plus_one = NEAR_LEVEL_2; + end + + NEAR_LEVEL_2: begin + near_level_plus_one = NEAR_LEVEL_4; + end + + NEAR_LEVEL_4: begin + near_level_plus_one = NEAR_LEVEL_8; + end + + NEAR_LEVEL_8: begin + near_level_plus_one = NEAR_LEVEL_16; + end + + default: begin + near_level_plus_one = NEAR_LEVEL_31; + end + endcase + end + + always_comb begin + near_level_plus_two = current_near_level; + case (current_near_level) + NEAR_LEVEL_0: begin + near_level_plus_two = NEAR_LEVEL_2; + end + + NEAR_LEVEL_1: begin + near_level_plus_two = NEAR_LEVEL_4; + end + + NEAR_LEVEL_2: begin + near_level_plus_two = NEAR_LEVEL_8; + end + + NEAR_LEVEL_4: begin + near_level_plus_two = NEAR_LEVEL_16; + end + + default: begin + near_level_plus_two = NEAR_LEVEL_31; + end + endcase + end + + always_comb begin + near_level_minus_one = current_near_level; + case (current_near_level) + NEAR_LEVEL_1: begin + near_level_minus_one = NEAR_LEVEL_0; + end + + NEAR_LEVEL_2: begin + near_level_minus_one = NEAR_LEVEL_1; + end + + NEAR_LEVEL_4: begin + near_level_minus_one = NEAR_LEVEL_2; + end + + NEAR_LEVEL_8: begin + near_level_minus_one = NEAR_LEVEL_4; + end + + NEAR_LEVEL_16: begin + near_level_minus_one = NEAR_LEVEL_8; + end + + NEAR_LEVEL_31: begin + near_level_minus_one = NEAR_LEVEL_16; + end + + default: begin + near_level_minus_one = NEAR_LEVEL_0; + end + endcase + end + + always_comb begin + near_level_minus_two = current_near_level; + case (current_near_level) + NEAR_LEVEL_0: begin + near_level_minus_two = NEAR_LEVEL_0; + end + + NEAR_LEVEL_1: begin + near_level_minus_two = NEAR_LEVEL_0; + end + + NEAR_LEVEL_2: begin + near_level_minus_two = NEAR_LEVEL_0; + end + + NEAR_LEVEL_4: begin + near_level_minus_two = NEAR_LEVEL_1; + end + + NEAR_LEVEL_8: begin + near_level_minus_two = NEAR_LEVEL_2; + end + + NEAR_LEVEL_16: begin + near_level_minus_two = NEAR_LEVEL_4; + end + + default: begin + near_level_minus_two = NEAR_LEVEL_8; + end + endcase + end + + always_comb begin + adjusted_near_level_next = current_near_level; + if (step_up_two_levels) begin + adjusted_near_level_next = near_level_plus_two; + end else if (step_up_one_level) begin + adjusted_near_level_next = near_level_plus_one; + end else if (step_down_two_levels) begin + adjusted_near_level_next = near_level_minus_two; + end else if (step_down_one_level) begin + adjusted_near_level_next = near_level_minus_one; + end + end + + always_comb begin + near_level_is_max = 1'b0; + if (current_near_level == NEAR_LEVEL_31) begin + near_level_is_max = 1'b1; + end + end + + always_comb begin + target_still_missed_at_max = 1'b0; + if (near_level_is_max && (pending_actual_bits_sum > pending_target_bits_sum)) begin + target_still_missed_at_max = 1'b1; end end always_ff @(posedge clk) begin if (rst) begin active_ratio <= RATIO_LOSSLESS; - current_near <= 6'd0; + current_near_level <= NEAR_LEVEL_0; actual_bits_cumulative <= 48'd0; target_bits_cumulative <= 48'd0; target_miss_at_max_near <= 1'b0; @@ -222,10 +478,11 @@ module jls_near_ctrl #( pending_actual_bits_sum <= 48'd0; pending_target_bits_sum <= 48'd0; pending_ratio_is_lossless_or_invalid <= 1'b1; + pending_first_strip_update <= 1'b0; end else begin if (image_start_valid) begin active_ratio <= image_ratio; - current_near <= 6'd0; + current_near_level <= NEAR_LEVEL_0; actual_bits_cumulative <= 48'd0; target_bits_cumulative <= 48'd0; target_miss_at_max_near <= 1'b0; @@ -233,19 +490,20 @@ module jls_near_ctrl #( pending_actual_bits_sum <= 48'd0; pending_target_bits_sum <= 48'd0; pending_ratio_is_lossless_or_invalid <= 1'b1; + pending_first_strip_update <= 1'b0; end else if (pending_update_valid) begin actual_bits_cumulative <= pending_actual_bits_sum; target_bits_cumulative <= pending_target_bits_sum; if (pending_ratio_is_lossless_or_invalid) begin - current_near <= 6'd0; - end else if (near_can_increase) begin - current_near <= current_near + 6'd1; - end else if (near_can_decrease) begin - current_near <= current_near - 6'd1; + current_near_level <= NEAR_LEVEL_0; + end else if (pending_first_strip_update) begin + current_near_level <= first_strip_level_next; + end else begin + current_near_level <= adjusted_near_level_next; end - if (!pending_ratio_is_lossless_or_invalid && actual_over_target && near_is_max) begin + if (target_still_missed_at_max) begin target_miss_at_max_near <= 1'b1; end @@ -255,6 +513,8 @@ module jls_near_ctrl #( pending_actual_bits_sum <= actual_bits_sum; pending_target_bits_sum <= target_bits_sum; pending_ratio_is_lossless_or_invalid <= ratio_is_lossless_or_invalid; + pending_first_strip_update <= (actual_bits_cumulative == 48'd0) && + (target_bits_cumulative == 48'd0); end end end