Files
retroSoC/rtl/ip/multimedia/jpeg_mcu_reconstructor.sv
T
maksyuki 2840d22e96 feat: enhance JPEG processing modules and tests
- Updated  to integrate a coefficient engine and additional signals for coefficient handling.
- Modified  to include a coefficient engine, enhancing the encoding process with new signals.
- Improved  to validate generated blocks against expected values and added timeout handling.
- Introduced  for testing SRAM wrappers in JPEG workspace.
- Enhanced test coverage in  to reject invalid STA net drivers.
- Updated  to ensure proper coverage of canonical domains in timing contracts.
- Added a new test for JPEG coefficient engine in .
- Improved memory map tests in  to validate public active MMIO regions.
- Enhanced management core specification tests in  to verify high-performance core specifications.
- Added behavioral-only checks in  to exclude synthesis and netlist consumers.
- Updated SDRAM tests in  to track active firmware memory.
- Enhanced STA constraints tests in  to cover current clock domains and net drivers.
2026-09-01 22:33:35 +08:00

383 lines
12 KiB
Systemverilog

// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND.
module jpeg_mcu_reconstructor (
// verilog_format: off -- preserve command, block, and raster-stream interface columns
input logic clk_i,
input logic rst_n_i,
input logic start_i,
input logic [ 1:0] sampling_i,
input logic [ 2:0] format_i,
input logic [ 4:0] valid_width_i,
input logic [ 4:0] valid_height_i,
input logic [511:0] block_i,
input logic block_valid_i,
output logic block_ready_o,
output logic start_ready_o,
axi4_stream_if.source pixel_axis,
output logic done_o,
output logic error_o
// verilog_format: on
);
typedef enum logic [2:0] {
Idle,
Load,
Pixels,
Nv12Chroma,
Done
} state_e;
state_e s_state_d;
state_e s_state_q;
logic [ 2:0] s_state_bits_q;
logic [ 1:0] s_sampling_d;
logic [ 1:0] s_sampling_q;
logic [ 2:0] s_format_d;
logic [ 2:0] s_format_q;
logic [ 4:0] s_width_d;
logic [ 4:0] s_width_q;
logic [ 4:0] s_height_d;
logic [ 4:0] s_height_q;
logic [ 2:0] s_block_cnt_d;
logic [ 2:0] s_block_cnt_q;
logic [ 5:0][511:0] s_blocks_q;
logic [ 4:0] s_x_d;
logic [ 4:0] s_x_q;
logic [ 4:0] s_y_d;
logic [ 4:0] s_y_q;
logic s_err_d;
logic s_err_q;
logic [ 2:0] s_expected_blocks;
logic [ 7:0] s_y0;
logic [ 7:0] s_y1;
logic [ 7:0] s_cb0;
logic [ 7:0] s_cb1;
logic [ 7:0] s_cr0;
logic [ 7:0] s_cr1;
logic [ 7:0] s_red0;
logic [ 7:0] s_green0;
logic [ 7:0] s_blue0;
logic [ 7:0] s_red1;
logic [ 7:0] s_green1;
logic [ 7:0] s_blue1;
logic [15:0] s_rgb565_0;
logic [15:0] s_rgb565_1;
logic s_second_valid;
logic s_pixel_accept;
logic [ 4:0] s_x_next;
logic [ 5:0] s_block_write_en;
function automatic logic [7:0] block_sample(input logic [511:0] block_i, input logic [2:0] x_i,
input logic [2:0] y_i);
return block_i[((y_i*8)+x_i)*8+:8];
endfunction
function automatic logic [7:0] clamp_color(input logic signed [17:0] value_i);
if (value_i < 0) begin
return 8'd0;
end
if (value_i > 255) begin
return 8'd255;
end
return value_i[7:0];
endfunction
function automatic logic [23:0] ycbcr_to_rgb(input logic [7:0] y_i, input logic [7:0] cb_i,
input logic [7:0] cr_i);
logic signed [ 9:0] s_cb;
logic signed [ 9:0] s_cr;
logic signed [17:0] s_red;
logic signed [17:0] s_green;
logic signed [17:0] s_blue;
begin
s_cb = $signed({1'b0, cb_i}) - 10'sd128;
s_cr = $signed({1'b0, cr_i}) - 10'sd128;
s_red = $signed(18'({1'b0, y_i})) + ((18'sd359 * s_cr + 18'sd128) >>> 8);
s_green = $signed(18'({1'b0, y_i})) - ((18'sd88 * s_cb + 18'sd183 * s_cr + 18'sd128) >>> 8);
s_blue = $signed(18'({1'b0, y_i})) + ((18'sd454 * s_cb + 18'sd128) >>> 8);
return {clamp_color(s_red), clamp_color(s_green), clamp_color(s_blue)};
end
endfunction
function automatic logic [7:0] average_chroma(input logic [7:0] left_i,
input logic [7:0] right_i);
logic [8:0] s_sum;
begin
s_sum = {1'b0, left_i} + {1'b0, right_i} + 1'b1;
return s_sum[8:1];
end
endfunction
always_comb begin
unique case (s_sampling_q)
2'd0: s_expected_blocks = 3'd1;
2'd1: s_expected_blocks = 3'd3;
2'd2: s_expected_blocks = 3'd4;
default: s_expected_blocks = 3'd6;
endcase
end
always_comb begin
s_y0 = 8'd0;
s_y1 = 8'd0;
s_cb0 = 8'd128;
s_cb1 = 8'd128;
s_cr0 = 8'd128;
s_cr1 = 8'd128;
unique case (s_sampling_q)
2'd0: begin
s_y0 = block_sample(s_blocks_q[0], s_x_q[2:0], s_y_q[2:0]);
s_y1 = block_sample(s_blocks_q[0], s_x_next[2:0], s_y_q[2:0]);
end
2'd1: begin
s_y0 = block_sample(s_blocks_q[0], s_x_q[2:0], s_y_q[2:0]);
s_y1 = block_sample(s_blocks_q[0], s_x_next[2:0], s_y_q[2:0]);
s_cb0 = block_sample(s_blocks_q[1], s_x_q[2:0], s_y_q[2:0]);
s_cb1 = block_sample(s_blocks_q[1], s_x_next[2:0], s_y_q[2:0]);
s_cr0 = block_sample(s_blocks_q[2], s_x_q[2:0], s_y_q[2:0]);
s_cr1 = block_sample(s_blocks_q[2], s_x_next[2:0], s_y_q[2:0]);
end
2'd2: begin
s_y0 = block_sample(s_blocks_q[s_x_q[3]], s_x_q[2:0], s_y_q[2:0]);
s_y1 = block_sample(s_blocks_q[s_x_next[3]], s_x_next[2:0], s_y_q[2:0]);
s_cb0 = block_sample(s_blocks_q[2], s_x_q[3:1], s_y_q[2:0]);
s_cb1 = block_sample(s_blocks_q[2], s_x_next[3:1], s_y_q[2:0]);
s_cr0 = block_sample(s_blocks_q[3], s_x_q[3:1], s_y_q[2:0]);
s_cr1 = block_sample(s_blocks_q[3], s_x_next[3:1], s_y_q[2:0]);
end
default: begin
s_y0 = block_sample(s_blocks_q[{s_y_q[3], s_x_q[3]}], s_x_q[2:0], s_y_q[2:0]);
s_y1 = block_sample(s_blocks_q[{s_y_q[3], s_x_next[3]}], s_x_next[2:0], s_y_q[2:0]);
s_cb0 = block_sample(s_blocks_q[4], s_x_q[3:1], s_y_q[3:1]);
s_cb1 = block_sample(s_blocks_q[4], s_x_next[3:1], s_y_q[3:1]);
s_cr0 = block_sample(s_blocks_q[5], s_x_q[3:1], s_y_q[3:1]);
s_cr1 = block_sample(s_blocks_q[5], s_x_next[3:1], s_y_q[3:1]);
end
endcase
end
assign {s_red0, s_green0, s_blue0} = ycbcr_to_rgb(s_y0, s_cb0, s_cr0);
assign {s_red1, s_green1, s_blue1} = ycbcr_to_rgb(s_y1, s_cb1, s_cr1);
assign s_rgb565_0 = {s_red0[7:3], s_green0[7:2], s_blue0[7:3]};
assign s_rgb565_1 = {s_red1[7:3], s_green1[7:2], s_blue1[7:3]};
assign s_x_next = s_x_q + 1'b1;
assign s_second_valid = (s_x_q + 1'b1) < s_width_q;
assign s_pixel_accept = pixel_axis.tvalid && pixel_axis.tready;
assign s_state_q = state_e'(s_state_bits_q);
assign start_ready_o = s_state_q == Idle;
assign block_ready_o = s_state_q == Load;
assign done_o = s_state_q == Done;
assign error_o = s_err_q;
assign s_block_write_en = ((s_state_q == Load) && block_valid_i) ?
(6'b000001 << s_block_cnt_q) : 6'b000000;
// Every required block is loaded before Pixels, so the block bank needs no reset.
for (genvar block_index = 0; block_index < 6; block_index++) begin : gen_block_register
dffl #(
.DATA_WIDTH(512)
) u_block_dffl (
.clk_i(clk_i),
.en_i (s_block_write_en[block_index]),
.dat_i(block_i),
.dat_o(s_blocks_q[block_index])
);
end
always_comb begin
pixel_axis.tdata = '0;
pixel_axis.tkeep = '0;
pixel_axis.tstrb = '0;
pixel_axis.tlast = 1'b0;
pixel_axis.tid = '0;
pixel_axis.tdest = '0;
pixel_axis.tuser = '0;
pixel_axis.tvalid = (s_state_q == Pixels) || (s_state_q == Nv12Chroma);
if (s_state_q == Nv12Chroma) begin
pixel_axis.tdata[7:0] = s_cb0;
pixel_axis.tdata[15:8] = s_cr0;
pixel_axis.tkeep = 8'h03;
pixel_axis.tstrb = 8'h03;
pixel_axis.tlast = (s_x_q + 2 >= s_width_q);
pixel_axis.tdest = 1'b1;
pixel_axis.tuser = (s_x_q == 5'd0) && (s_y_q == 5'd0);
end else begin
unique case (s_format_q)
3'd0: begin
pixel_axis.tdata[7:0] = s_y0;
pixel_axis.tdata[15:8] = s_y1;
pixel_axis.tkeep = s_second_valid ? 8'h03 : 8'h01;
end
3'd1: begin
pixel_axis.tdata[15:0] = s_rgb565_0;
pixel_axis.tdata[31:16] = s_rgb565_1;
pixel_axis.tkeep = s_second_valid ? 8'h0f : 8'h03;
end
3'd2: begin
pixel_axis.tdata[23:0] = {s_blue0, s_green0, s_red0};
pixel_axis.tdata[47:24] = {s_blue1, s_green1, s_red1};
pixel_axis.tkeep = s_second_valid ? 8'h3f : 8'h07;
end
3'd3: begin
pixel_axis.tdata[31:0] = {
average_chroma(s_cr0, s_cr1), s_y1, average_chroma(s_cb0, s_cb1), s_y0
};
pixel_axis.tkeep = 8'h0f;
end
default: begin
pixel_axis.tdata[7:0] = s_y0;
pixel_axis.tdata[15:8] = s_y1;
pixel_axis.tkeep = s_second_valid ? 8'h03 : 8'h01;
end
endcase
pixel_axis.tstrb = pixel_axis.tkeep;
pixel_axis.tlast = (s_x_q + 2 >= s_width_q);
pixel_axis.tuser = (s_x_q == 5'd0) && (s_y_q == 5'd0);
end
end
always_comb begin
s_state_d = s_state_q;
s_sampling_d = s_sampling_q;
s_format_d = s_format_q;
s_width_d = s_width_q;
s_height_d = s_height_q;
s_block_cnt_d = s_block_cnt_q;
s_x_d = s_x_q;
s_y_d = s_y_q;
s_err_d = s_err_q;
unique case (s_state_q)
Idle: begin
if (start_i) begin
s_sampling_d = sampling_i;
s_format_d = format_i;
s_width_d = valid_width_i;
s_height_d = valid_height_i;
s_block_cnt_d = 3'd0;
s_x_d = 5'd0;
s_y_d = 5'd0;
s_err_d = (format_i > 3'd4) || (valid_width_i == 5'd0) ||
(valid_height_i == 5'd0) || (valid_width_i > 5'd16) ||
(valid_height_i > 5'd16);
s_state_d = Load;
end
end
Load: begin
if (block_valid_i) begin
if (s_block_cnt_q + 1'b1 == s_expected_blocks) begin
s_x_d = 5'd0;
s_y_d = 5'd0;
s_state_d = Pixels;
end else begin
s_block_cnt_d = s_block_cnt_q + 1'b1;
end
end
end
Pixels: begin
if (s_pixel_accept) begin
if (s_x_q + 2 >= s_width_q) begin
s_x_d = 5'd0;
if (s_y_q + 1'b1 >= s_height_q) begin
s_y_d = 5'd0;
s_state_d = (s_format_q == 3'd4 && s_sampling_q != 2'd0) ? Nv12Chroma : Done;
end else begin
s_y_d = s_y_q + 1'b1;
end
end else begin
s_x_d = s_x_q + 2;
end
end
end
Nv12Chroma: begin
if (s_pixel_accept) begin
if (s_x_q + 2 >= s_width_q) begin
s_x_d = 5'd0;
if (s_y_q + 2 >= s_height_q) begin
s_state_d = Done;
end else begin
s_y_d = s_y_q + 2;
end
end else begin
s_x_d = s_x_q + 2;
end
end
end
Done: begin
s_state_d = Idle;
end
default: s_state_d = Idle;
endcase
end
dffr #(
.DATA_WIDTH(3)
) u_state_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_state_d),
.dat_o (s_state_bits_q)
);
dffr #(
.DATA_WIDTH(2)
) u_sampling_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_sampling_d),
.dat_o (s_sampling_q)
);
dffr #(
.DATA_WIDTH(3)
) u_format_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_format_d),
.dat_o (s_format_q)
);
dffr #(
.DATA_WIDTH(5)
) u_width_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_width_d),
.dat_o (s_width_q)
);
dffr #(
.DATA_WIDTH(5)
) u_height_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_height_d),
.dat_o (s_height_q)
);
dffr #(
.DATA_WIDTH(3)
) u_block_cnt_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_block_cnt_d),
.dat_o (s_block_cnt_q)
);
dffr #(
.DATA_WIDTH(5)
) u_x_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_x_d),
.dat_o (s_x_q)
);
dffr #(
.DATA_WIDTH(5)
) u_y_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_y_d),
.dat_o (s_y_q)
);
dffr u_err_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_err_d),
.dat_o (s_err_q)
);
endmodule