feat: add tests for JPEG reference model and register synchronization

- Implemented tests for the deterministic baseline JPEG reference model in , covering encoding, decoding, quality scaling, and error handling.
- Created  to ensure synchronization between RTL and C register constants for JPEG.
- Updated  to reflect changes in the number of APB interfaces and IRQ mappings, ensuring the topology generates correct bindings.
This commit is contained in:
maksyuki committed 2026-08-31 19:23:19 +08:00
1 parent d164a562ad
commit 39bc1c84b4
75 files changed
+15048 -58

No files matched your search

+182
View File
@@ -0,0 +1,182 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
module apb4_jpeg_ring_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic [63:0] memory [0:2047];
logic read_active;
logic [31:0] read_base;
logic [ 7:0] read_len;
logic [ 7:0] read_index;
logic write_active;
logic [31:0] write_base;
logic [ 7:0] write_index;
logic bvalid;
logic irq;
apb4_if apb4 (
.pclk (clk),
.presetn(rst_n)
);
axi4_if #(
.DATA_WIDTH(64),
.ID_WIDTH (3)
) axi4 (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
apb4_jpeg u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.quiesce_i (1'b0),
.resource_reset_i(1'b0),
.apb4 (apb4),
.axi4 (axi4),
.idle_o (),
.irq_o (irq)
);
assign axi4.arready = !read_active;
assign axi4.rid = 3'd0;
assign axi4.rdata = memory[(read_base>>3)+read_index];
assign axi4.rresp = `AXI4_RESP_OKAY;
assign axi4.rlast = read_index == read_len;
assign axi4.ruser = '0;
assign axi4.rvalid = read_active;
assign axi4.awready = !write_active && !bvalid;
assign axi4.wready = write_active;
assign axi4.bid = 3'd0;
assign axi4.bresp = `AXI4_RESP_OKAY;
assign axi4.buser = '0;
assign axi4.bvalid = bvalid;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
read_active <= 1'b0;
read_base <= '0;
read_len <= '0;
read_index <= '0;
write_active <= 1'b0;
write_base <= '0;
write_index <= '0;
bvalid <= 1'b0;
end else begin
if (axi4.arvalid && axi4.arready) begin
read_active <= 1'b1;
read_base <= axi4.araddr;
read_len <= axi4.arlen;
read_index <= 8'd0;
end else if (axi4.rvalid && axi4.rready) begin
if (axi4.rlast) begin
read_active <= 1'b0;
end else begin
read_index <= read_index + 1'b1;
end
end
if (axi4.awvalid && axi4.awready) begin
write_active <= 1'b1;
write_base <= axi4.awaddr;
write_index <= 8'd0;
end
if (axi4.wvalid && axi4.wready) begin
for (int byte_index = 0; byte_index < 8; byte_index++) begin
if (axi4.wstrb[byte_index]) begin
memory[(write_base>>3)+write_index][byte_index*8+:8] <= axi4.wdata[byte_index*8+:8];
end
end
if (axi4.wlast) begin
write_active <= 1'b0;
bvalid <= 1'b1;
end else begin
write_index <= write_index + 1'b1;
end
end
if (axi4.bvalid && axi4.bready) begin
bvalid <= 1'b0;
end
end
end
task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] data_i);
begin
@(negedge clk);
apb4.paddr = {20'd0, offset_i};
apb4.pwdata = data_i;
apb4.pstrb = 4'hf;
apb4.pwrite = 1'b1;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk);
apb4.penable = 1'b1;
#1;
if (!apb4.pready || apb4.pslverr) $fatal(1, "APB write failed at %h", offset_i);
@(negedge clk);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
task automatic apb_read(input logic [11:0] offset_i, output logic [31:0] data_o);
begin
@(negedge clk);
apb4.paddr = {20'd0, offset_i};
apb4.pwrite = 1'b0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk);
apb4.penable = 1'b1;
#1;
if (!apb4.pready || apb4.pslverr) $fatal(1, "APB read failed at %h", offset_i);
data_o = apb4.prdata;
@(negedge clk);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
logic [31:0] value;
int timeout;
for (int index = 0; index < 2048; index++) begin
memory[index] = 64'd0;
end
memory[32'h1000>>3] = 64'h0000000000000001;
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
apb_write(12'h01c, 32'h12);
apb_write(12'h100, 32'h00001000);
apb_write(12'h104, 32'd2);
apb_write(12'h10c, 32'd1);
apb_write(12'h110, 32'd1);
apb_write(12'h11c, 32'd1);
timeout = 2000;
value = 32'd1;
while ((value[0] != 1'b0) && (timeout > 0)) begin
apb_read(12'h014, value);
timeout--;
end
if (timeout == 0) $fatal(1, "ring job did not complete");
apb_read(12'h108, value);
if (value != 32'd1) $fatal(1, "ring head was not advanced");
apb_read(12'h018, value);
if ((value & 32'h12) != 32'h12 || !irq) $fatal(1, "ring IRQ status mismatch");
if (memory[32'h1000>>3][31:0] != 32'd0 || memory[32'h1000>>3][63:32] != 32'h00000103) begin
$fatal(1, "descriptor error writeback mismatch: %h", memory[32'h1000>>3]);
end
$display("JPEG APB ring tests passed");
$finish;
end
endmodule
+89
View File
@@ -0,0 +1,89 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_bit_packer_tb;
logic clk;
logic rst_n;
logic [127:0] token_bits;
logic [ 23:0] token_length;
logic [ 2:0] token_count;
logic token_valid;
logic token_ready;
logic flush;
logic flush_ready;
logic flush_done;
logic [ 63:0] byte_data;
logic [ 7:0] byte_keep;
logic byte_valid;
logic byte_last;
logic byte_ready;
logic error_flag;
jpeg_bit_packer u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.token_bits_i (token_bits),
.token_length_i(token_length),
.token_count_i (token_count),
.token_valid_i (token_valid),
.token_ready_o (token_ready),
.flush_i (flush),
.flush_ready_o (flush_ready),
.flush_done_o (flush_done),
.byte_data_o (byte_data),
.byte_keep_o (byte_keep),
.byte_valid_o (byte_valid),
.byte_last_o (byte_last),
.byte_ready_i (byte_ready),
.error_o (error_flag)
);
always #5 clk = ~clk;
initial begin
clk = 1'b0;
rst_n = 1'b0;
token_bits = '0;
token_length = '0;
token_count = 3'd0;
token_valid = 1'b0;
flush = 1'b0;
byte_ready = 1'b0;
repeat (3) @(negedge clk);
rst_n = 1'b1;
token_bits[0+:32] = 32'hff;
token_length[0+:6] = 6'd8;
token_bits[32+:32] = 32'ha;
token_length[6+:6] = 6'd4;
token_count = 3'd2;
token_valid = 1'b1;
@(negedge clk);
if (!token_ready) $fatal(1, "token input was not accepted");
token_valid = 1'b0;
while (!flush_ready) @(negedge clk);
flush = 1'b1;
@(negedge clk);
flush = 1'b0;
while (!byte_valid) @(negedge clk);
repeat (2) begin
@(negedge clk);
if (byte_data[23:0] != 24'haf00ff || byte_keep != 8'h07 || !byte_last) begin
$fatal(1, "stuffed output changed under backpressure");
end
end
byte_ready = 1'b1;
#1;
if (!flush_done || error_flag) $fatal(1, "flush completion mismatch");
@(negedge clk);
byte_ready = 1'b0;
if (!flush_ready) $fatal(1, "packer did not return to running state");
flush = 1'b1;
@(negedge clk);
flush = 1'b0;
if (!flush_done || byte_valid) $fatal(1, "empty flush completion mismatch");
@(negedge clk);
$display("JPEG bit packer tests passed");
$finish;
end
endmodule
+111
View File
@@ -0,0 +1,111 @@
`timescale 1ns / 1ps
module jpeg_bit_reader_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic [ 7:0] byte_data;
logic byte_valid;
logic byte_ready;
logic byte_last;
logic [ 5:0] bit_consume;
logic bit_consume_valid;
logic align_bits;
logic [31:0] bit_window;
logic [ 5:0] bit_count;
logic marker_valid;
logic [ 7:0] marker;
logic marker_ready;
logic unpack_error;
logic reader_error;
axi4_stream_if #(
.DATA_WIDTH(64)
) input_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
jpeg_byte_unpacker u_unpacker (
.clk_i (clk),
.rst_n_i (rst_n),
.input_axis (input_axis),
.byte_o (byte_data),
.byte_valid_o(byte_valid),
.byte_ready_i(byte_ready),
.byte_last_o (byte_last),
.error_o (unpack_error)
);
jpeg_bit_reader u_reader (
.clk_i (clk),
.rst_n_i (rst_n),
.byte_i (byte_data),
.byte_valid_i (byte_valid),
.byte_ready_o (byte_ready),
.byte_last_i (byte_last),
.bit_consume_i (bit_consume),
.bit_consume_valid_i(bit_consume_valid),
.align_i (align_bits),
.bit_window_o (bit_window),
.bit_count_o (bit_count),
.marker_valid_o (marker_valid),
.marker_o (marker),
.marker_ready_i (marker_ready),
.error_o (reader_error)
);
initial begin
input_axis.tdata = 64'hd9ff7fd0ffaf00ff;
input_axis.tkeep = 8'hff;
input_axis.tstrb = 8'hff;
input_axis.tlast = 1'b1;
input_axis.tid = '0;
input_axis.tdest = '0;
input_axis.tuser = '0;
input_axis.tvalid = 1'b0;
bit_consume = 6'd0;
bit_consume_valid = 1'b0;
align_bits = 1'b0;
marker_ready = 1'b0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
input_axis.tvalid = 1'b1;
#1;
if (!input_axis.tready) $fatal(1, "input AXIS beat was not accepted");
@(negedge clk);
input_axis.tvalid = 1'b0;
while (!marker_valid) @(negedge clk);
if (marker != 8'hd0 || bit_count != 6'd16 || bit_window[31:16] != 16'hffaf) begin
$fatal(1, "restart boundary mismatch: marker=%h count=%0d window=%h", marker, bit_count,
bit_window);
end
bit_consume = 6'd12;
bit_consume_valid = 1'b1;
@(negedge clk);
bit_consume_valid = 1'b0;
if (bit_count != 6'd4 || bit_window[31:28] != 4'hf) begin
$fatal(1, "bit consumption mismatch");
end
align_bits = 1'b1;
marker_ready = 1'b1;
@(negedge clk);
align_bits = 1'b0;
marker_ready = 1'b0;
while (!marker_valid) @(negedge clk);
if (marker != 8'hd9 || bit_count != 6'd8 || bit_window[31:24] != 8'h7f) begin
$fatal(1, "EOI boundary mismatch: marker=%h count=%0d window=%h", marker, bit_count,
bit_window);
end
align_bits = 1'b1;
marker_ready = 1'b1;
@(negedge clk);
if (unpack_error || reader_error) $fatal(1, "unexpected byte/bit reader error");
$display("JPEG byte unpacker and bit reader tests passed");
$finish;
end
endmodule
+93
View File
@@ -0,0 +1,93 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_block_decoder_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [ 511:0] quant;
logic [1599:0] reciprocal;
logic [ 191:0] dc_code;
logic [ 59:0] dc_length;
logic [4095:0] ac_code;
logic [1279:0] ac_length;
logic [ 31:0] bit_window;
logic [ 5:0] bit_count;
logic [ 5:0] bit_consume;
logic bit_consume_valid;
logic [ 511:0] block_out;
logic signed [ 23:0] dc_out;
logic result_valid;
logic result_ready;
logic error_flag;
always #5 clk = ~clk;
jpeg_block_decoder u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.previous_dc_i (24'sd2),
.quant_i (quant),
.reciprocal_i (reciprocal),
.dc_code_i (dc_code),
.dc_length_i (dc_length),
.ac_code_i (ac_code),
.ac_length_i (ac_length),
.bit_window_i (bit_window),
.bit_count_i (bit_count),
.bit_consume_o (bit_consume),
.bit_consume_valid_o(bit_consume_valid),
.start_ready_o (),
.block_o (block_out),
.dc_o (dc_out),
.result_valid_o (result_valid),
.result_ready_i (result_ready),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && bit_consume_valid) begin
bit_window <= bit_window << bit_consume;
bit_count <= bit_count - bit_consume;
end
end
initial begin
start = 1'b0;
quant = {64{8'd1}};
reciprocal = {64{25'd16777216}};
dc_code = '0;
dc_length = '0;
ac_code = '0;
ac_length = '0;
bit_window = {23'b10111_1110_1110_1110_101011, 9'd0};
bit_count = 6'd23;
result_ready = 1'b0;
dc_code[2*16+:16] = 16'b101;
dc_length[2*5+:5] = 5'd3;
ac_code[8'hf0*16+:16] = 16'b1110;
ac_length[8'hf0*5+:5] = 5'd4;
ac_code[8'he1*16+:16] = 16'b10101;
ac_length[8'he1*5+:5] = 5'd5;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!result_valid) @(negedge clk);
if (error_flag || dc_out != 24'sd5 || bit_count != 6'd0) begin
$fatal(1, "block decode status mismatch");
end
for (int index = 0; index < 64; index++) begin
if (block_out[index*8+:8] != 8'd129) begin
$fatal(1, "decoded sample mismatch at %0d: %0d", index, block_out[index*8+:8]);
end
end
result_ready = 1'b1;
@(negedge clk);
$display("JPEG block decoder tests passed");
$finish;
end
endmodule
+104
View File
@@ -0,0 +1,104 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_block_encoder_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [ 511:0] block_in;
logic [ 511:0] quant;
logic [1599:0] reciprocal;
logic [ 191:0] dc_code;
logic [ 59:0] dc_length;
logic [4095:0] ac_code;
logic [1279:0] ac_length;
logic [ 127:0] token_bits;
logic [ 23:0] token_length;
logic [ 2:0] token_count;
logic token_valid;
logic token_last;
logic signed [ 23:0] dc_out;
logic result_valid;
logic result_ready;
logic error_flag;
int token_index;
always #5 clk = ~clk;
jpeg_block_encoder u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.block_i (block_in),
.previous_dc_i (24'sd0),
.quant_i (quant),
.reciprocal_i (reciprocal),
.dc_code_i (dc_code),
.dc_length_i (dc_length),
.ac_code_i (ac_code),
.ac_length_i (ac_length),
.start_ready_o (),
.token_bits_o (token_bits),
.token_length_o(token_length),
.token_count_o (token_count),
.token_valid_o (token_valid),
.token_last_o (token_last),
.token_ready_i (1'b1),
.dc_o (dc_out),
.result_valid_o(result_valid),
.result_ready_i(result_ready),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && token_valid) begin
if (token_index == 0) begin
if (token_count != 3'd1 || token_bits[0+:32] != 32'd88 ||
token_length[0+:6] != 6'd7 || token_last) begin
$fatal(1, "DC token mismatch");
end
end else if (token_index == 1) begin
if (token_count != 3'd1 || token_bits[0+:32] != 32'd3 ||
token_length[0+:6] != 6'd2 || !token_last) begin
$fatal(1, "EOB token mismatch: count=%0d bits=%0d len=%0d last=%0d", token_count,
token_bits[0+:32], token_length[0+:6], token_last);
end
end else begin
$fatal(1, "unexpected token beat");
end
token_index <= token_index + 1;
end
end
initial begin
start = 1'b0;
block_in = {64{8'd129}};
quant = {64{8'd1}};
reciprocal = {64{25'd16777216}};
dc_code = '0;
dc_length = '0;
ac_code = '0;
ac_length = '0;
dc_code[4*16+:16] = 16'b101;
dc_length[4*5+:5] = 5'd3;
ac_code[0+:16] = 16'b11;
ac_length[0+:5] = 5'd2;
result_ready = 1'b0;
token_index = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!result_valid) @(negedge clk);
if (error_flag || dc_out != 24'sd8 || token_index != 2) begin
$fatal(1, "block encode status mismatch: err=%0d dc=%0d tokens=%0d", error_flag, dc_out,
token_index);
end
result_ready = 1'b1;
@(negedge clk);
$display("JPEG block encoder tests passed");
$finish;
end
endmodule
+91
View File
@@ -0,0 +1,91 @@
`timescale 1ns / 1ps
module jpeg_byte_joiner_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic [63:0] input_data;
logic [ 7:0] input_keep;
logic input_valid;
logic input_ready;
logic input_last;
logic error_flag;
int output_beats;
axi4_stream_if #(
.DATA_WIDTH(64)
) output_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
jpeg_byte_joiner u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.input_data_i (input_data),
.input_keep_i (input_keep),
.input_valid_i(input_valid),
.input_ready_o(input_ready),
.input_last_i (input_last),
.output_axis (output_axis),
.error_o (error_flag)
);
task automatic send_beat(input logic [63:0] data_i, input logic [7:0] keep_i, input logic last_i);
begin
@(negedge clk);
input_data = data_i;
input_keep = keep_i;
input_last = last_i;
input_valid = 1'b1;
@(posedge clk);
while (!input_ready) @(posedge clk);
@(negedge clk);
input_valid = 1'b0;
end
endtask
always @(posedge clk) begin
if (rst_n && output_axis.tvalid && output_axis.tready) begin
if (output_beats == 0) begin
if (output_axis.tdata != 64'ha2a1a00504030201 || output_axis.tkeep != 8'hff ||
output_axis.tlast) begin
$fatal(1, "joined full beat mismatch");
end
end else if (output_beats == 1) begin
if (output_axis.tdata[15:0] != 16'hd9ff || output_axis.tkeep != 8'h03 ||
!output_axis.tlast) begin
$fatal(1, "joined final beat mismatch");
end
end else begin
$fatal(1, "unexpected joined output beat");
end
output_beats <= output_beats + 1;
end
end
initial begin
input_data = '0;
input_keep = '0;
input_valid = 1'b0;
input_last = 1'b0;
output_axis.tready = 1'b0;
output_beats = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
send_beat(64'h0000000504030201, 8'h1f, 1'b0);
send_beat(64'h0000000000a2a1a0, 8'h07, 1'b0);
repeat (2) @(negedge clk);
if (!output_axis.tvalid || output_axis.tdata != 64'ha2a1a00504030201) begin
$fatal(1, "full output was not held under backpressure: valid=%0d data=%h keep=%h",
output_axis.tvalid, output_axis.tdata, output_axis.tkeep);
end
output_axis.tready = 1'b1;
send_beat(64'h000000000000d9ff, 8'h03, 1'b1);
while (output_beats < 2) @(negedge clk);
if (error_flag) $fatal(1, "unexpected byte joiner error");
$display("JPEG byte joiner tests passed");
$finish;
end
endmodule
+139
View File
@@ -0,0 +1,139 @@
`timescale 1ns / 1ps
module jpeg_decode_core_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic busy;
logic done;
logic [15:0] width;
logic [15:0] height;
logic [ 1:0] sampling;
logic error_flag;
logic [ 4:0] error_code;
logic [ 7:0] input_bytes [0:2047];
logic [ 7:0] expected_rgb [0:4095];
integer input_size;
integer expected_size;
integer output_index;
string input_path;
string expected_path;
axi4_stream_if #(
.DATA_WIDTH(64)
) bitstream_axis (
.aclk (clk),
.aresetn(rst_n)
);
axi4_stream_if #(
.DATA_WIDTH(64)
) pixel_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
jpeg_decode_core u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.output_format_i(3'd2),
.bitstream_axis (bitstream_axis),
.pixel_axis (pixel_axis),
.start_ready_o (),
.busy_o (busy),
.done_o (done),
.width_o (width),
.height_o (height),
.sampling_o (sampling),
.error_o (error_flag),
.error_code_o (error_code)
);
task automatic send_input_beat(input integer offset_i);
logic [63:0] data;
logic [ 7:0] keep;
begin
data = '0;
keep = '0;
for (int lane = 0; lane < 8; lane++) begin
if ((offset_i + lane) < input_size) begin
data[lane*8+:8] = input_bytes[offset_i+lane];
keep[lane] = 1'b1;
end
end
@(negedge clk);
bitstream_axis.tdata = data;
bitstream_axis.tkeep = keep;
bitstream_axis.tstrb = keep;
bitstream_axis.tlast = (offset_i + 8) >= input_size;
bitstream_axis.tvalid = 1'b1;
@(posedge clk);
while (!bitstream_axis.tready) @(posedge clk);
@(negedge clk);
bitstream_axis.tvalid = 1'b0;
end
endtask
always @(posedge clk) begin
if (rst_n && pixel_axis.tvalid && pixel_axis.tready) begin
for (int lane = 0; lane < 8; lane++) begin
if (pixel_axis.tkeep[lane]) begin
if (pixel_axis.tdata[lane*8+:8] != expected_rgb[output_index]) begin
$fatal(1, "decoded byte mismatch at %0d: got=%h expected=%h", output_index,
pixel_axis.tdata[lane*8+:8], expected_rgb[output_index]);
end
output_index = output_index + 1;
end
end
end
end
initial begin
if (!$value$plusargs(
"input_hex=%s", input_path
) || !$value$plusargs(
"input_size=%d", input_size
) || !$value$plusargs(
"expected_rgb_hex=%s", expected_path
) || !$value$plusargs(
"expected_rgb_size=%d", expected_size
)) begin
$fatal(1, "decode core test requires JPEG and RGB plusargs");
end
$readmemh(input_path, input_bytes);
$readmemh(expected_path, expected_rgb);
start = 1'b0;
bitstream_axis.tdata = '0;
bitstream_axis.tkeep = '0;
bitstream_axis.tstrb = '0;
bitstream_axis.tlast = 1'b0;
bitstream_axis.tid = '0;
bitstream_axis.tdest = '0;
bitstream_axis.tuser = '0;
bitstream_axis.tvalid = 1'b0;
pixel_axis.tready = 1'b1;
output_index = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
for (int offset = 0; offset < input_size; offset += 8) begin
send_input_beat(offset);
end
for (int cycle = 0; cycle < 200000 && !done && !error_flag; cycle++) begin
@(negedge clk);
end
if (error_flag || !done || width != 16'd16 || height != 16'd16 || sampling != 2'd3 ||
output_index != expected_size) begin
$fatal(1,
"decode completion mismatch: done=%0d err=%0d code=%0d size=%0dx%0d sampling=%0d bytes=%0d expected=%0d",
done, error_flag, error_code, width, height, sampling, output_index, expected_size);
end
$display("JPEG decode core tests passed");
$finish;
end
endmodule
+198
View File
@@ -0,0 +1,198 @@
`timescale 1ns / 1ps
`include "axi4_define.svh"
module jpeg_dma_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic read_start;
logic write_start;
logic read_busy;
logic write_busy;
logic read_done;
logic write_done;
logic error_flag;
logic [31:0] read_bytes;
logic [31:0] write_bytes;
logic [63:0] memory [0:2047];
logic read_active;
logic [31:0] read_base;
logic [ 7:0] read_len;
logic [ 7:0] read_index;
logic write_active;
logic [31:0] write_base;
logic [ 7:0] write_len;
logic [ 7:0] write_index;
logic bvalid;
int read_bursts;
axi4_if #(
.DATA_WIDTH(64),
.ID_WIDTH (3)
) axi4 (
.aclk (clk),
.aresetn(rst_n)
);
axi4_stream_if #(
.DATA_WIDTH(64)
) read_axis (
.aclk (clk),
.aresetn(rst_n)
);
axi4_stream_if #(
.DATA_WIDTH(64)
) write_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
assign write_axis.tdata = read_axis.tdata;
assign write_axis.tkeep = read_axis.tkeep;
assign write_axis.tstrb = read_axis.tstrb;
assign write_axis.tlast = read_axis.tlast;
assign write_axis.tid = '0;
assign write_axis.tdest = '0;
assign write_axis.tuser = read_axis.tuser;
assign write_axis.tvalid = read_axis.tvalid;
assign read_axis.tready = write_axis.tready;
jpeg_dma u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.abort_i (1'b0),
.quiesce_i (1'b0),
.read_start_i (read_start),
.read_addr_i (32'h00000ff0),
.read_line_bytes_i (32'd20),
.read_stride_i (32'd32),
.read_lines_i (16'd2),
.read_busy_o (read_busy),
.read_done_o (read_done),
.read_axis (read_axis),
.write_start_i (write_start),
.write_addr_i (32'h00002000),
.write_line_bytes_i(32'd20),
.write_stride_i (32'd32),
.write_lines_i (16'd2),
.write_busy_o (write_busy),
.write_done_o (write_done),
.write_axis (write_axis),
.error_o (error_flag),
.error_read_o (),
.error_resp_o (),
.error_addr_o (),
.read_bytes_o (read_bytes),
.write_bytes_o (write_bytes),
.read_stall_o (),
.write_stall_o (),
.axi4 (axi4)
);
assign axi4.arready = !read_active;
assign axi4.rid = 3'd0;
assign axi4.rdata = memory[(read_base>>3)+read_index];
assign axi4.rresp = `AXI4_RESP_OKAY;
assign axi4.rlast = read_index == read_len;
assign axi4.ruser = '0;
assign axi4.rvalid = read_active;
assign axi4.awready = !write_active && !bvalid;
assign axi4.wready = write_active;
assign axi4.bid = 3'd0;
assign axi4.bresp = `AXI4_RESP_OKAY;
assign axi4.buser = '0;
assign axi4.bvalid = bvalid;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
read_active <= 1'b0;
read_base <= '0;
read_len <= '0;
read_index <= '0;
write_active <= 1'b0;
write_base <= '0;
write_len <= '0;
write_index <= '0;
bvalid <= 1'b0;
read_bursts <= 0;
end else begin
if (axi4.arvalid && axi4.arready) begin
read_active <= 1'b1;
read_base <= axi4.araddr;
read_len <= axi4.arlen;
read_index <= 8'd0;
read_bursts <= read_bursts + 1;
end else if (axi4.rvalid && axi4.rready) begin
if (axi4.rlast) begin
read_active <= 1'b0;
end else begin
read_index <= read_index + 1'b1;
end
end
if (axi4.awvalid && axi4.awready) begin
write_active <= 1'b1;
write_base <= axi4.awaddr;
write_len <= axi4.awlen;
write_index <= 8'd0;
end
if (axi4.wvalid && axi4.wready) begin
for (int byte_index = 0; byte_index < 8; byte_index++) begin
if (axi4.wstrb[byte_index]) begin
memory[(write_base>>3)+write_index][byte_index*8+:8] <= axi4.wdata[byte_index*8+:8];
end
end
if (axi4.wlast) begin
write_active <= 1'b0;
bvalid <= 1'b1;
end else begin
write_index <= write_index + 1'b1;
end
end
if (axi4.bvalid && axi4.bready) begin
bvalid <= 1'b0;
end
end
end
initial begin
for (int index = 0; index < 2048; index++) begin
memory[index] = 64'hdeadbeefdeadbeef;
end
memory[32'h0ff0>>3] = 64'h0706050403020100;
memory[32'h0ff8>>3] = 64'h0f0e0d0c0b0a0908;
memory[32'h1000>>3] = 64'h1716151413121110;
memory[32'h1010>>3] = 64'h2726252423222120;
memory[32'h1018>>3] = 64'h2f2e2d2c2b2a2928;
memory[32'h1020>>3] = 64'h3736353433323130;
read_start = 1'b0;
write_start = 1'b0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
read_start = 1'b1;
write_start = 1'b1;
@(negedge clk);
read_start = 1'b0;
write_start = 1'b0;
while (read_busy || write_busy) @(negedge clk);
if (error_flag || read_bytes != 32'd40 || write_bytes != 32'd40) begin
$fatal(1, "DMA status mismatch: err=%0d read=%0d write=%0d", error_flag, read_bytes,
write_bytes);
end
if (read_bursts < 3) $fatal(1, "4 KiB or line boundary was not split");
if (memory[32'h2000>>3] != 64'h0706050403020100 ||
memory[32'h2008>>3] != 64'h0f0e0d0c0b0a0908 ||
memory[32'h2010>>3][31:0] != 32'h13121110 ||
memory[32'h2020>>3] != 64'h2726252423222120 ||
memory[32'h2028>>3] != 64'h2f2e2d2c2b2a2928 ||
memory[32'h2030>>3][31:0] != 32'h33323130) begin
$fatal(1, "2D DMA payload mismatch");
end
if (memory[32'h2010>>3][63:32] != 32'hdeadbeef ||
memory[32'h2030>>3][63:32] != 32'hdeadbeef) begin
$fatal(1, "tail strobe corrupted memory");
end
$display("JPEG DMA tests passed");
$finish;
end
endmodule
+219
View File
@@ -0,0 +1,219 @@
`timescale 1ns / 1ps
module jpeg_encode_core_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [ 511:0] luma_quant;
logic [1599:0] luma_reciprocal;
logic [ 191:0] luma_dc_code;
logic [ 59:0] luma_dc_length;
logic [4095:0] luma_ac_code;
logic [1279:0] luma_ac_length;
logic [ 511:0] chroma_quant;
logic [1599:0] chroma_reciprocal;
logic [ 191:0] chroma_dc_code;
logic [ 59:0] chroma_dc_length;
logic [4095:0] chroma_ac_code;
logic [1279:0] chroma_ac_length;
logic busy;
logic done;
logic error_flag;
logic [ 7:0] expected [0:2047];
logic [ 7:0] luma_quant_values [ 0:63];
logic [ 7:0] chroma_quant_values [ 0:63];
logic [ 31:0] luma_dc_entries [ 0:11];
logic [ 31:0] chroma_dc_entries [ 0:11];
logic [ 31:0] luma_ac_entries [ 0:255];
logic [ 31:0] chroma_ac_entries [ 0:255];
integer expected_size;
integer output_index;
integer input_beats;
integer expected_input_beats;
integer test_width;
integer test_height;
integer cycle_count;
integer timeout_cycles;
string expected_path;
string luma_quant_path;
string chroma_quant_path;
string luma_dc_path;
string chroma_dc_path;
string luma_ac_path;
string chroma_ac_path;
axi4_stream_if #(
.DATA_WIDTH(64)
) pixel_axis (
.aclk (clk),
.aresetn(rst_n)
);
axi4_stream_if #(
.DATA_WIDTH(64)
) bitstream_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
jpeg_encode_core u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.width_i (test_width[15:0]),
.height_i (test_height[15:0]),
.sampling_i (2'd3),
.input_format_i (3'd2),
.restart_interval_i (16'd0),
.luma_quant_i (luma_quant),
.luma_reciprocal_i (luma_reciprocal),
.luma_dc_code_i (luma_dc_code),
.luma_dc_length_i (luma_dc_length),
.luma_ac_code_i (luma_ac_code),
.luma_ac_length_i (luma_ac_length),
.chroma_quant_i (chroma_quant),
.chroma_reciprocal_i(chroma_reciprocal),
.chroma_dc_code_i (chroma_dc_code),
.chroma_dc_length_i (chroma_dc_length),
.chroma_ac_code_i (chroma_ac_code),
.chroma_ac_length_i (chroma_ac_length),
.pixel_axis (pixel_axis),
.bitstream_axis (bitstream_axis),
.start_ready_o (),
.busy_o (busy),
.done_o (done),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && busy) begin
cycle_count <= cycle_count + 1;
end
if (rst_n && pixel_axis.tvalid && pixel_axis.tready) begin
input_beats <= input_beats + 1;
end
if (rst_n && bitstream_axis.tvalid && bitstream_axis.tready) begin
for (int lane = 0; lane < 8; lane++) begin
if (bitstream_axis.tkeep[lane]) begin
if (bitstream_axis.tdata[lane*8+:8] != expected[output_index]) begin
$fatal(1, "encoded byte mismatch at %0d: got=%h expected=%h", output_index,
bitstream_axis.tdata[lane*8+:8], expected[output_index]);
end
output_index = output_index + 1;
end
end
if (bitstream_axis.tlast != (output_index == expected_size)) begin
$fatal(1, "encoded TLAST mismatch");
end
end
end
initial begin
if (!$value$plusargs(
"expected_hex=%s", expected_path
) || !$value$plusargs(
"expected_size=%d", expected_size
) || !$value$plusargs(
"luma_quant_hex=%s", luma_quant_path
) || !$value$plusargs(
"chroma_quant_hex=%s", chroma_quant_path
) || !$value$plusargs(
"luma_dc_hex=%s", luma_dc_path
) || !$value$plusargs(
"chroma_dc_hex=%s", chroma_dc_path
) || !$value$plusargs(
"luma_ac_hex=%s", luma_ac_path
) || !$value$plusargs(
"chroma_ac_hex=%s", chroma_ac_path
)) begin
$fatal(1, "encode core test requires JPEG and table plusargs");
end
test_width = 16;
test_height = 16;
expected_input_beats = 96;
void'($value$plusargs("test_width=%d", test_width));
void'($value$plusargs("test_height=%d", test_height));
void'($value$plusargs("input_beats=%d", expected_input_beats));
$readmemh(expected_path, expected);
$readmemh(luma_quant_path, luma_quant_values);
$readmemh(chroma_quant_path, chroma_quant_values);
$readmemh(luma_dc_path, luma_dc_entries);
$readmemh(chroma_dc_path, chroma_dc_entries);
$readmemh(luma_ac_path, luma_ac_entries);
$readmemh(chroma_ac_path, chroma_ac_entries);
luma_quant = '0;
luma_reciprocal = '0;
chroma_quant = '0;
chroma_reciprocal = '0;
luma_dc_code = '0;
luma_dc_length = '0;
chroma_dc_code = '0;
chroma_dc_length = '0;
luma_ac_code = '0;
luma_ac_length = '0;
chroma_ac_code = '0;
chroma_ac_length = '0;
for (int index = 0; index < 64; index++) begin
luma_quant[index*8+:8] = luma_quant_values[index];
chroma_quant[index*8+:8] = chroma_quant_values[index];
luma_reciprocal[index*25+:25] =
(25'd16777216 + luma_quant_values[index] - 1'b1) / luma_quant_values[index];
chroma_reciprocal[index*25+:25] =
(25'd16777216 + chroma_quant_values[index] - 1'b1) / chroma_quant_values[index];
end
for (int index = 0; index < 12; index++) begin
luma_dc_code[index*16+:16] = luma_dc_entries[index][15:0];
luma_dc_length[index*5+:5] = luma_dc_entries[index][20:16];
chroma_dc_code[index*16+:16] = chroma_dc_entries[index][15:0];
chroma_dc_length[index*5+:5] = chroma_dc_entries[index][20:16];
end
for (int index = 0; index < 256; index++) begin
luma_ac_code[index*16+:16] = luma_ac_entries[index][15:0];
luma_ac_length[index*5+:5] = luma_ac_entries[index][20:16];
chroma_ac_code[index*16+:16] = chroma_ac_entries[index][15:0];
chroma_ac_length[index*5+:5] = chroma_ac_entries[index][20:16];
end
start = 1'b0;
pixel_axis.tdata = {8{8'd128}};
pixel_axis.tkeep = 8'hff;
pixel_axis.tstrb = 8'hff;
pixel_axis.tlast = 1'b0;
pixel_axis.tid = '0;
pixel_axis.tdest = '0;
pixel_axis.tuser = '0;
pixel_axis.tvalid = 1'b0;
bitstream_axis.tready = 1'b1;
output_index = 0;
input_beats = 0;
cycle_count = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
pixel_axis.tvalid = 1'b1;
while (input_beats < expected_input_beats) begin
@(negedge clk);
pixel_axis.tlast = input_beats + 1 == expected_input_beats;
end
pixel_axis.tvalid = 1'b0;
pixel_axis.tlast = 1'b0;
timeout_cycles = 3000;
while (!done && (timeout_cycles > 0)) begin
@(negedge clk);
timeout_cycles--;
end
if (timeout_cycles == 0) begin
$fatal(1, "encode timeout: core=%0d builder=%0d block=%0d", u_dut.s_state_q,
u_dut.u_mcu_builder.s_state_q, u_dut.u_block_encoder.s_state_q);
end
if (error_flag || output_index != expected_size) begin
$fatal(1, "encode completion mismatch: err=%0d bytes=%0d expected=%0d", error_flag,
output_index, expected_size);
end
$display("JPEG encode core tests passed cycles=%0d", cycle_count);
$finish;
end
endmodule
+95
View File
@@ -0,0 +1,95 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_entropy_decoder_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic signed [ 23:0] previous_dc;
logic [ 191:0] dc_code;
logic [ 59:0] dc_length;
logic [4095:0] ac_code;
logic [1279:0] ac_length;
logic [ 31:0] bit_window;
logic [ 5:0] bit_count;
logic [ 5:0] bit_consume;
logic bit_consume_valid;
logic start_ready;
logic [1535:0] block_out;
logic signed [ 23:0] dc_out;
logic result_valid;
logic result_ready;
logic error_flag;
always #5 clk = ~clk;
jpeg_entropy_decoder u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.previous_dc_i (previous_dc),
.dc_code_i (dc_code),
.dc_length_i (dc_length),
.ac_code_i (ac_code),
.ac_length_i (ac_length),
.bit_window_i (bit_window),
.bit_count_i (bit_count),
.bit_consume_o (bit_consume),
.bit_consume_valid_o(bit_consume_valid),
.start_ready_o (start_ready),
.block_o (block_out),
.dc_o (dc_out),
.result_valid_o (result_valid),
.result_ready_i (result_ready),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && bit_consume_valid) begin
bit_window <= bit_window << bit_consume;
bit_count <= bit_count - bit_consume;
end
end
initial begin
start = 1'b0;
previous_dc = 24'sd2;
dc_code = '0;
dc_length = '0;
ac_code = '0;
ac_length = '0;
bit_window = {23'b10111_1110_1110_1110_101011, 9'd0};
bit_count = 6'd23;
result_ready = 1'b0;
dc_code[2*16+:16] = 16'b101;
dc_length[2*5+:5] = 5'd3;
ac_code[8'hf0*16+:16] = 16'b1110;
ac_length[8'hf0*5+:5] = 5'd4;
ac_code[8'he1*16+:16] = 16'b10101;
ac_length[8'he1*5+:5] = 5'd5;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!result_valid) @(negedge clk);
if (error_flag || dc_out != 24'sd5 || $signed(
block_out[0+:24]
) != 24'sd5 || $signed(
block_out[63*24+:24]
) != 24'sd1 || bit_count != 6'd0) begin
$fatal(1, "entropy decode mismatch: err=%0d dc=%0d last=%0d bits=%0d", error_flag, dc_out,
$signed(block_out[63*24+:24]), bit_count);
end
for (int index = 1; index < 63; index++) begin
if ($signed(block_out[index*24+:24]) != 24'sd0) begin
$fatal(1, "unexpected coefficient at %0d", index);
end
end
result_ready = 1'b1;
@(negedge clk);
$display("JPEG entropy decoder tests passed");
$finish;
end
endmodule
+115
View File
@@ -0,0 +1,115 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_entropy_encoder_tb;
logic clk;
logic rst_n;
logic start;
logic [1535:0] block_in;
logic signed [ 23:0] previous_dc;
logic [ 191:0] dc_code;
logic [ 59:0] dc_length;
logic [4095:0] ac_code;
logic [1279:0] ac_length;
logic start_ready;
logic signed [ 23:0] dc_out;
logic [ 127:0] token_bits;
logic [ 23:0] token_length;
logic [ 2:0] token_count;
logic token_valid;
logic token_last;
logic token_ready;
logic error_flag;
logic [ 31:0] captured_bits [0:4];
logic [ 5:0] captured_length[0:4];
int capture_count;
jpeg_entropy_encoder u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.block_i (block_in),
.previous_dc_i (previous_dc),
.dc_code_i (dc_code),
.dc_length_i (dc_length),
.ac_code_i (ac_code),
.ac_length_i (ac_length),
.start_ready_o (start_ready),
.dc_o (dc_out),
.token_bits_o (token_bits),
.token_length_o(token_length),
.token_count_o (token_count),
.token_valid_o (token_valid),
.token_last_o (token_last),
.token_ready_i (token_ready),
.error_o (error_flag)
);
always #5 clk = ~clk;
always @(posedge clk) begin
if (rst_n && token_valid && token_ready) begin
for (int index = 0; index < token_count; index++) begin
captured_bits[capture_count] = token_bits[index*32+:32];
captured_length[capture_count] = token_length[index*6+:6];
capture_count = capture_count + 1;
end
end
end
initial begin
clk = 1'b0;
rst_n = 1'b0;
start = 1'b0;
block_in = '0;
previous_dc = 24'sd2;
dc_code = '0;
dc_length = '0;
ac_code = '0;
ac_length = '0;
token_ready = 1'b0;
capture_count = 0;
block_in[0+:24] = 24'sd5;
block_in[63*24+:24] = 24'sd1;
dc_code[2*16+:16] = 16'b101;
dc_length[2*5+:5] = 5'd3;
ac_code[8'hf0*16+:16] = 16'b1110;
ac_length[8'hf0*5+:5] = 5'd4;
ac_code[8'he1*16+:16] = 16'b10101;
ac_length[8'he1*5+:5] = 5'd5;
repeat (3) @(negedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!token_valid) @(negedge clk);
repeat (2) begin
@(negedge clk);
if (token_bits[0+:32] != 32'd23 || token_length[0+:6] != 6'd5) begin
$fatal(1, "token changed under backpressure");
end
end
token_ready = 1'b1;
while (!start_ready) @(negedge clk);
@(negedge clk);
if (error_flag) $fatal(1, "unexpected entropy error");
if (dc_out != 24'sd5 || capture_count != 5) begin
$fatal(1, "entropy completion mismatch: dc=%0d tokens=%0d", dc_out, capture_count);
end
if (captured_bits[0] != 32'd23 || captured_length[0] != 6'd5) begin
$fatal(1, "DC token mismatch");
end
for (int index = 1; index < 4; index++) begin
if (captured_bits[index] != 32'd14 || captured_length[index] != 6'd4) begin
$fatal(1, "ZRL token mismatch at %0d", index);
end
end
if (captured_bits[4] != 32'd43 || captured_length[4] != 6'd6) begin
$fatal(1, "final AC token mismatch");
end
$display("JPEG entropy encoder tests passed");
$finish;
end
endmodule
+96
View File
@@ -0,0 +1,96 @@
`timescale 1ns / 1ps
module jpeg_header_writer_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [511:0] luma_quant;
logic [511:0] chroma_quant;
logic [ 7:0] output_byte;
logic output_valid;
logic output_ready;
logic done;
logic error_flag;
logic [ 7:0] expected [0:1023];
logic [ 7:0] luma_values [ 0:63];
logic [ 7:0] chroma_values [ 0:63];
integer expected_size;
integer output_index;
string header_path;
string luma_path;
string chroma_path;
always #5 clk = ~clk;
jpeg_header_writer u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.width_i (16'd17),
.height_i (16'd15),
.sampling_i (2'd3),
.restart_interval_i(16'd1),
.luma_quant_i (luma_quant),
.chroma_quant_i (chroma_quant),
.start_ready_o (),
.byte_o (output_byte),
.byte_valid_o (output_valid),
.byte_ready_i (output_ready),
.done_o (done),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && output_valid && output_ready) begin
if (output_byte != expected[output_index]) begin
$fatal(1, "header byte mismatch at %0d: got=%h expected=%h", output_index, output_byte,
expected[output_index]);
end
output_index <= output_index + 1;
end
end
initial begin
if (!$value$plusargs(
"header_hex=%s", header_path
) || !$value$plusargs(
"luma_hex=%s", luma_path
) || !$value$plusargs(
"chroma_hex=%s", chroma_path
) || !$value$plusargs(
"header_size=%d", expected_size
)) begin
$fatal(1, "header writer test requires header and quantization plusargs");
end
$readmemh(header_path, expected);
$readmemh(luma_path, luma_values);
$readmemh(chroma_path, chroma_values);
luma_quant = '0;
chroma_quant = '0;
for (int index = 0; index < 64; index++) begin
luma_quant[index*8+:8] = luma_values[index];
chroma_quant[index*8+:8] = chroma_values[index];
end
start = 1'b0;
output_ready = 1'b0;
output_index = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
repeat (2) @(negedge clk);
if (!output_valid || output_byte != 8'hff) begin
$fatal(1, "header output changed under backpressure");
end
output_ready = 1'b1;
while (!done) @(negedge clk);
if (error_flag || output_index != expected_size) begin
$fatal(1, "header completion mismatch: err=%0d bytes=%0d expected=%0d", error_flag,
output_index, expected_size);
end
$display("JPEG header writer tests passed");
$finish;
end
endmodule
+145
View File
@@ -0,0 +1,145 @@
`timescale 1ns / 1ps
module jpeg_marker_parser_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [ 7:0] byte_data;
logic byte_valid;
logic byte_ready;
logic [ 3:0] table_kind;
logic [ 7:0] table_index;
logic [31:0] table_data;
logic table_write;
logic table_commit;
logic header_valid;
logic header_ready;
logic entropy;
logic [15:0] width;
logic [15:0] height;
logic [ 1:0] sampling;
logic [ 1:0] component_count;
logic [15:0] restart_interval;
logic error_flag;
logic [ 4:0] error_code;
logic [ 7:0] input_bytes [0:4095];
integer input_size;
integer input_index;
integer table_writes;
integer table_commits;
logic quant_zero_seen;
logic dc_zero_seen;
logic [31:0] actual_tables [0:3071];
logic [31:0] expected_tables [0:3071];
string hex_path;
string tables_path;
always #5 clk = ~clk;
assign byte_data = input_bytes[input_index];
assign byte_valid = input_index < input_size;
jpeg_marker_parser u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.table_context_i (2'd2),
.byte_i (byte_data),
.byte_valid_i (byte_valid),
.byte_ready_o (byte_ready),
.table_context_o (),
.table_kind_o (table_kind),
.table_index_o (table_index),
.table_data_o (table_data),
.table_write_o (table_write),
.table_commit_o (table_commit),
.header_valid_o (header_valid),
.header_ready_i (header_ready),
.entropy_o (entropy),
.width_o (width),
.height_o (height),
.sampling_o (sampling),
.component_count_o (component_count),
.component_id_o (),
.component_factor_o(),
.quant_table_o (),
.dc_table_o (),
.ac_table_o (),
.restart_interval_o(restart_interval),
.marker_count_o (),
.error_o (error_flag),
.error_code_o (error_code)
);
always @(posedge clk) begin
if (rst_n && byte_valid && byte_ready) begin
input_index <= input_index + 1;
end
if (rst_n && table_write) begin
actual_tables[(table_kind*256)+table_index] <= table_data;
table_writes <= table_writes + 1;
if ((table_kind == 4'd0) && (table_index == 8'd0) && (table_data[7:0] == 8'd8)) begin
quant_zero_seen <= 1'b1;
end
if ((table_kind == 4'd4) && (table_index == 8'd0) && (table_data == 32'h00020000)) begin
dc_zero_seen <= 1'b1;
end
end
if (rst_n && table_commit) begin
table_commits <= table_commits + 1;
end
end
initial begin
if (!$value$plusargs(
"jpeg_hex=%s", hex_path
) || !$value$plusargs(
"tables_hex=%s", tables_path
) || !$value$plusargs(
"jpeg_size=%d", input_size
)) begin
$fatal(1, "JPEG parser test requires jpeg_hex and jpeg_size plusargs");
end
$readmemh(hex_path, input_bytes);
$readmemh(tables_path, expected_tables);
input_index = 0;
table_writes = 0;
table_commits = 0;
quant_zero_seen = 1'b0;
dc_zero_seen = 1'b0;
for (int index = 0; index < 3072; index++) begin
actual_tables[index] = 32'd0;
end
start = 1'b0;
header_ready = 1'b0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!header_valid && !error_flag) @(negedge clk);
if (error_flag)
$fatal(1, "header parser failed with code %0d at byte %0d", error_code, input_index);
if (width != 16'd17 || height != 16'd15 || sampling != 2'd3 ||
component_count != 2'd3 || restart_interval != 16'd1 || table_writes != 476 ||
table_commits != 1 || !quant_zero_seen || !dc_zero_seen) begin
$fatal(
1,
"header result mismatch: %0dx%0d sampling=%0d components=%0d restart=%0d writes=%0d commits=%0d q=%0d dc=%0d",
width, height, sampling, component_count, restart_interval, table_writes, table_commits,
quant_zero_seen, dc_zero_seen);
end
for (int index = 0; index < 3072; index++) begin
if (actual_tables[index] != expected_tables[index]) begin
$fatal(1, "expanded table mismatch at %0d: got=%h expected=%h", index,
actual_tables[index], expected_tables[index]);
end
end
header_ready = 1'b1;
@(negedge clk);
if (!entropy) $fatal(1, "parser did not enter entropy mode");
$display("JPEG marker parser tests passed");
$finish;
end
endmodule
+86
View File
@@ -0,0 +1,86 @@
`timescale 1ns / 1ps
module jpeg_mcu_builder_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [511:0] block_data;
logic block_valid;
logic block_last;
logic done;
logic error_flag;
int input_beats;
int output_blocks;
axi4_stream_if #(
.DATA_WIDTH(64)
) pixel_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
jpeg_mcu_builder u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.sampling_i (2'd3),
.format_i (3'd2),
.valid_width_i (5'd16),
.valid_height_i(5'd16),
.pixel_axis (pixel_axis),
.start_ready_o (),
.block_o (block_data),
.block_valid_o (block_valid),
.block_ready_i (1'b1),
.block_last_o (block_last),
.done_o (done),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && pixel_axis.tvalid && pixel_axis.tready) begin
input_beats <= input_beats + 1;
end
if (rst_n && block_valid) begin
if (block_data != {64{8'd128}} || (block_last != (output_blocks == 5))) begin
$fatal(1, "generated block mismatch at %0d", output_blocks);
end
output_blocks <= output_blocks + 1;
end
end
initial begin
start = 1'b0;
pixel_axis.tdata = {8{8'd128}};
pixel_axis.tkeep = 8'hff;
pixel_axis.tstrb = 8'hff;
pixel_axis.tlast = 1'b0;
pixel_axis.tid = '0;
pixel_axis.tdest = '0;
pixel_axis.tuser = '0;
pixel_axis.tvalid = 1'b0;
input_beats = 0;
output_blocks = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
pixel_axis.tvalid = 1'b1;
while (input_beats < 96) begin
@(negedge clk);
pixel_axis.tlast = input_beats == 95;
end
pixel_axis.tvalid = 1'b0;
pixel_axis.tlast = 1'b0;
while (!done) @(negedge clk);
if (error_flag || output_blocks != 6) begin
$fatal(1, "MCU builder completion mismatch: err=%0d blocks=%0d", error_flag, output_blocks);
end
$display("JPEG MCU builder tests passed");
$finish;
end
endmodule
+90
View File
@@ -0,0 +1,90 @@
`timescale 1ns / 1ps
module jpeg_mcu_reconstructor_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic start;
logic [511:0] block_data;
logic block_valid;
logic block_ready;
logic done;
logic error_flag;
int block_index;
int beat_count;
int line_count;
axi4_stream_if #(
.DATA_WIDTH(64)
) pixel_axis (
.aclk (clk),
.aresetn(rst_n)
);
always #5 clk = ~clk;
jpeg_mcu_reconstructor u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.sampling_i (2'd3),
.format_i (3'd2),
.valid_width_i (5'd16),
.valid_height_i(5'd16),
.block_i (block_data),
.block_valid_i (block_valid),
.block_ready_o (block_ready),
.start_ready_o (),
.pixel_axis (pixel_axis),
.done_o (done),
.error_o (error_flag)
);
always @(posedge clk) begin
if (rst_n && block_valid && block_ready) begin
block_index <= block_index + 1;
end
if (rst_n && pixel_axis.tvalid && pixel_axis.tready) begin
if (pixel_axis.tkeep != 8'h3f || pixel_axis.tdata[47:0] != 48'h808080808080) begin
$fatal(1, "RGB888 raster output mismatch");
end
if (pixel_axis.tuser != (beat_count == 0)) begin
$fatal(1, "SOF marker mismatch at beat %0d", beat_count);
end
if (pixel_axis.tlast) begin
line_count <= line_count + 1;
end
beat_count <= beat_count + 1;
end
end
initial begin
start = 1'b0;
block_data = {64{8'd128}};
block_valid = 1'b0;
pixel_axis.tready = 1'b0;
block_index = 0;
beat_count = 0;
line_count = 0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
@(negedge clk);
start = 1'b1;
@(negedge clk);
start = 1'b0;
block_valid = 1'b1;
while (block_index < 6) @(negedge clk);
block_valid = 1'b0;
repeat (2) @(negedge clk);
if (!pixel_axis.tvalid || pixel_axis.tdata[47:0] != 48'h808080808080) begin
$fatal(1, "pixel output not stable during backpressure");
end
pixel_axis.tready = 1'b1;
while (!done) @(negedge clk);
if (error_flag || beat_count != 128 || line_count != 16) begin
$fatal(1, "MCU completion mismatch: err=%0d beats=%0d lines=%0d", error_flag, beat_count,
line_count);
end
$display("JPEG MCU reconstructor tests passed");
$finish;
end
endmodule
+190
View File
@@ -0,0 +1,190 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_quantizer_tb;
logic clk;
logic rst_n;
logic start;
logic dequantize;
logic [1535:0] block_in;
logic [ 511:0] quant;
logic [1599:0] reciprocal;
logic start_ready;
logic [1535:0] block_out;
logic result_valid;
logic result_ready;
logic table_err;
logic overflow;
jpeg_quantizer u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.dequantize_i (dequantize),
.block_i (block_in),
.quant_i (quant),
.reciprocal_i (reciprocal),
.start_ready_o (start_ready),
.block_o (block_out),
.result_valid_o(result_valid),
.result_ready_i(result_ready),
.table_err_o (table_err),
.overflow_o (overflow)
);
always #5 clk = ~clk;
function automatic logic [7:0] luma_quant(input int index_i);
logic [7:0] values[0:63];
begin
values = '{
16,
11,
10,
16,
24,
40,
51,
61,
12,
12,
14,
19,
26,
58,
60,
55,
14,
13,
16,
24,
40,
57,
69,
56,
14,
17,
22,
29,
51,
87,
80,
62,
18,
22,
37,
56,
68,
109,
103,
77,
24,
35,
55,
64,
81,
104,
113,
92,
49,
64,
78,
87,
103,
121,
120,
101,
72,
92,
95,
98,
112,
100,
103,
99
};
return values[index_i];
end
endfunction
function automatic logic signed [23:0] coefficient(input int index_i);
begin
case (index_i)
0: coefficient = -24'sd4;
1: coefficient = -24'sd17;
3: coefficient = -24'sd3;
8: coefficient = -24'sd146;
24: coefficient = -24'sd15;
40: coefficient = -24'sd5;
56: coefficient = -24'sd1;
default: coefficient = 24'sd0;
endcase
end
endfunction
function automatic logic signed [23:0] quantized(input int index_i);
begin
case (index_i)
1: quantized = -24'sd2;
8: quantized = -24'sd12;
24: quantized = -24'sd1;
default: quantized = 24'sd0;
endcase
end
endfunction
task automatic run_operation(input logic dequantize_i);
begin
@(negedge clk);
dequantize = dequantize_i;
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!result_valid) @(negedge clk);
end
endtask
initial begin
clk = 1'b0;
rst_n = 1'b0;
start = 1'b0;
dequantize = 1'b0;
block_in = '0;
quant = '0;
reciprocal = '0;
result_ready = 1'b0;
for (int index = 0; index < 64; index++) begin
block_in[index*24+:24] = coefficient(index);
quant[index*8+:8] = luma_quant(index);
reciprocal[index*25+:25] = (25'd16777216 + luma_quant(index) - 1'b1) / luma_quant(index);
end
repeat (3) @(negedge clk);
rst_n = 1'b1;
run_operation(1'b0);
if (table_err || overflow) $fatal(1, "unexpected quantizer error");
for (int index = 0; index < 64; index++) begin
if ($signed(block_out[index*24+:24]) != quantized(index)) begin
$fatal(1, "quantized mismatch at %0d: %0d", index, $signed(block_out[index*24+:24]));
end
end
block_in = block_out;
result_ready = 1'b1;
@(negedge clk);
result_ready = 1'b0;
run_operation(1'b1);
if ($signed(
block_out[1*24+:24]
) != -24'sd22 || $signed(
block_out[8*24+:24]
) != -24'sd144 || $signed(
block_out[24*24+:24]
) != -24'sd14) begin
$fatal(1, "dequantized output mismatch");
end
result_ready = 1'b1;
@(negedge clk);
$display("JPEG quantizer tests passed");
$finish;
end
endmodule
+190
View File
@@ -0,0 +1,190 @@
`timescale 1ns / 1ps
module jpeg_reg_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic busy;
logic job_done;
logic error_event;
logic start_pulse;
logic table_write;
logic [7:0] table_index;
logic irq;
apb4_if apb4 (
.pclk (clk),
.presetn(rst_n)
);
always #5 clk = ~clk;
jpeg_reg u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.apb4 (apb4),
.busy_i (busy),
.ring_active_i (1'b0),
.encode_i (1'b1),
.quiesce_i (1'b0),
.job_done_i (job_done),
.ring_event_i (1'b0),
.header_ready_i (1'b0),
.abort_done_i (1'b0),
.error_event_i (error_event),
.error_code_i (5'd9),
.error_stage_i (4'd7),
.error_axi_resp_i (2'd2),
.error_addr_i (32'h38001234),
.error_detail_i (32'h55aa55aa),
.cycles_i (64'h123456789abcdef0),
.pixels_i (32'd99),
.input_bytes_i (32'd100),
.output_bytes_i (32'd50),
.read_stall_i (32'd4),
.write_stall_i (32'd5),
.result_size_i (32'd50),
.result_image_size_i(32'h00100020),
.result_format_i (32'd3),
.result_markers_i (32'd7),
.ring_head_i (32'd2),
.ring_status_i (32'd1),
.table_data_i (32'h11223344),
.table_status_i (32'd1),
.start_o (start_pulse),
.abort_o (),
.soft_reset_o (),
.ring_kick_o (),
.job_config_o (),
.image_size_o (),
.input_format_o (),
.output_format_o (),
.encode_config_o (),
.restart_interval_o (),
.bitstream_addr_o (),
.bitstream_size_o (),
.plane0_addr_o (),
.plane0_stride_o (),
.plane1_addr_o (),
.plane1_stride_o (),
.plane2_addr_o (),
.plane2_stride_o (),
.metadata_addr_o (),
.metadata_length_o (),
.ring_base_o (),
.ring_size_o (),
.ring_tail_o (),
.ring_control_o (),
.irq_coalesce_o (),
.table_context_o (),
.table_kind_o (),
.table_index_o (table_index),
.table_write_data_o (),
.table_write_o (table_write),
.table_commit_o (),
.table_default_o (),
.table_clear_o (),
.perf_enable_o (),
.perf_clear_o (),
.irq_o (irq)
);
task automatic apb_write(input logic [11:0] offset_i, input logic [31:0] data_i,
input logic [3:0] strobe_i, input logic expected_err_i);
begin
@(negedge clk);
apb4.paddr = {20'd0, offset_i};
apb4.pwdata = data_i;
apb4.pstrb = strobe_i;
apb4.pwrite = 1'b1;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk);
apb4.penable = 1'b1;
#1;
if (!apb4.pready || (apb4.pslverr != expected_err_i)) begin
$fatal(1, "APB write response mismatch at %h", offset_i);
end
@(negedge clk);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
task automatic apb_read(input logic [11:0] offset_i, output logic [31:0] data_o,
input logic expected_err_i);
begin
@(negedge clk);
apb4.paddr = {20'd0, offset_i};
apb4.pwrite = 1'b0;
apb4.psel = 1'b1;
apb4.penable = 1'b0;
@(negedge clk);
apb4.penable = 1'b1;
#1;
if (!apb4.pready || (apb4.pslverr != expected_err_i)) begin
$fatal(1, "APB read response mismatch at %h", offset_i);
end
data_o = apb4.prdata;
@(negedge clk);
apb4.psel = 1'b0;
apb4.penable = 1'b0;
end
endtask
initial begin
logic [31:0] value;
apb4.paddr = '0;
apb4.pprot = '0;
apb4.psel = 1'b0;
apb4.penable = 1'b0;
apb4.pwrite = 1'b0;
apb4.pwdata = '0;
apb4.pstrb = '0;
busy = 1'b0;
job_done = 1'b0;
error_event = 1'b0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
apb_read(12'h000, value, 1'b0);
if (value != 32'h4a504547) $fatal(1, "JPEG IP ID mismatch");
apb_write(12'h084, 32'h04380780, 4'hf, 1'b0);
apb_write(12'h084, 32'h00000021, 4'h1, 1'b0);
apb_read(12'h084, value, 1'b0);
if (value != 32'h04380721) $fatal(1, "byte-strobe update mismatch");
apb_write(12'h01c, 32'h1, 4'hf, 1'b0);
@(negedge clk);
job_done = 1'b1;
@(negedge clk);
job_done = 1'b0;
apb_read(12'h018, value, 1'b0);
if (value != 32'h1 || !irq) $fatal(1, "completion IRQ mismatch");
apb_write(12'h018, 32'h1, 4'h1, 1'b0);
if (irq) $fatal(1, "IRQ W1C failed");
@(negedge clk);
error_event = 1'b1;
@(negedge clk);
error_event = 1'b0;
apb_read(12'h024, value, 1'b0);
if (value[0] != 1'b1 || value[5:1] != 5'd9 || value[9:6] != 4'd7) begin
$fatal(1, "first-error status mismatch");
end
apb_read(12'h028, value, 1'b0);
if (value != 32'h38001234) $fatal(1, "first-error address mismatch");
apb_write(12'h208, 32'd7, 4'hf, 1'b0);
apb_write(12'h20c, 32'h11223344, 4'hf, 1'b0);
if (table_index != 8'd8) $fatal(1, "table portal did not auto-increment");
apb_write(12'h200, 32'd4, 4'hf, 1'b1);
apb_read(12'h010, value, 1'b1);
busy = 1'b1;
apb_write(12'h084, 32'd1, 4'hf, 1'b1);
apb_write(12'h010, 32'h1, 4'hf, 1'b1);
busy = 1'b0;
apb_write(12'h010, 32'h1, 4'hf, 1'b0);
$display("JPEG APB register tests passed");
$finish;
end
endmodule
+120
View File
@@ -0,0 +1,120 @@
`timescale 1ns / 1ps
module jpeg_table_store_tb;
logic clk = 1'b0;
logic rst_n = 1'b0;
logic [ 1:0] portal_context;
logic [ 3:0] portal_kind;
logic [ 7:0] portal_index;
logic [31:0] portal_write_data;
logic portal_write;
logic portal_commit;
logic portal_default;
logic portal_clear;
logic [31:0] portal_read_data;
logic [31:0] portal_status;
logic lookup;
logic [ 1:0] lookup_context;
logic [ 3:0] lookup_kind;
logic [ 7:0] lookup_index;
logic [31:0] lookup_data;
logic lookup_valid;
logic lookup_err;
always #5 clk = ~clk;
jpeg_table_store u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.portal_context_i (portal_context),
.portal_kind_i (portal_kind),
.portal_index_i (portal_index),
.portal_write_data_i(portal_write_data),
.portal_write_i (portal_write),
.portal_commit_i (portal_commit),
.portal_default_i (portal_default),
.portal_clear_i (portal_clear),
.portal_read_data_o (portal_read_data),
.portal_status_o (portal_status),
.lookup_i (lookup),
.lookup_context_i (lookup_context),
.lookup_kind_i (lookup_kind),
.lookup_index_i (lookup_index),
.lookup_data_o (lookup_data),
.lookup_valid_o (lookup_valid),
.lookup_err_o (lookup_err)
);
task automatic portal_pulse(input logic write_i, input logic commit_i, input logic clear_i);
begin
@(negedge clk);
portal_write = write_i;
portal_commit = commit_i;
portal_clear = clear_i;
@(negedge clk);
portal_write = 1'b0;
portal_commit = 1'b0;
portal_clear = 1'b0;
end
endtask
task automatic lookup_word(input logic [1:0] context_i, input logic [3:0] kind_i,
input logic [7:0] index_i, input logic [31:0] expected_i);
begin
@(negedge clk);
lookup_context = context_i;
lookup_kind = kind_i;
lookup_index = index_i;
lookup = 1'b1;
@(negedge clk);
lookup = 1'b0;
if (!lookup_valid || lookup_err || lookup_data != expected_i) begin
$fatal(1, "lookup mismatch: valid=%0d err=%0d data=%h", lookup_valid, lookup_err,
lookup_data);
end
end
endtask
initial begin
portal_context = 2'd0;
portal_kind = 4'd0;
portal_index = 8'd0;
portal_write_data = 32'd0;
portal_write = 1'b0;
portal_commit = 1'b0;
portal_default = 1'b0;
portal_clear = 1'b0;
lookup = 1'b0;
lookup_context = 2'd0;
lookup_kind = 4'd0;
lookup_index = 8'd0;
repeat (3) @(posedge clk);
rst_n = 1'b1;
portal_context = 2'd2;
portal_kind = 4'd0;
portal_index = 8'd7;
portal_write_data = 32'h01020304;
portal_pulse(1'b1, 1'b0, 1'b0);
portal_pulse(1'b0, 1'b1, 1'b0);
if (!portal_status[3]) $fatal(1, "context commit did not set valid");
lookup_word(2'd2, 4'd0, 8'd7, 32'h01020304);
portal_context = 2'd3;
portal_kind = 4'd11;
portal_index = 8'd255;
portal_write_data = 32'ha5a55a5a;
portal_pulse(1'b1, 1'b0, 1'b0);
portal_pulse(1'b0, 1'b1, 1'b0);
lookup_word(2'd3, 4'd11, 8'd255, 32'ha5a55a5a);
portal_kind = 4'd0;
portal_index = 8'd64;
portal_pulse(1'b1, 1'b0, 1'b0);
if (!portal_status[11]) $fatal(1, "invalid portal index was not recorded");
portal_pulse(1'b0, 1'b0, 1'b1);
if (portal_status[11] || portal_status[3]) $fatal(1, "context clear failed");
$display("JPEG table store tests passed");
$finish;
end
endmodule
+96
View File
@@ -0,0 +1,96 @@
// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
module jpeg_transform_tb;
logic clk;
logic rst_n;
logic start;
logic inverse;
logic [1535:0] block_in;
logic start_ready;
logic [1535:0] block_out;
logic result_valid;
logic result_ready;
jpeg_transform u_dut (
.clk_i (clk),
.rst_n_i (rst_n),
.start_i (start),
.inverse_i (inverse),
.block_i (block_in),
.start_ready_o (start_ready),
.block_o (block_out),
.result_valid_o(result_valid),
.result_ready_i(result_ready)
);
always #5 clk = ~clk;
task automatic run_transform(input logic direction_i);
begin
@(negedge clk);
inverse = direction_i;
start = 1'b1;
@(negedge clk);
start = 1'b0;
while (!result_valid) @(negedge clk);
end
endtask
function automatic logic signed [15:0] expected_coefficient(input int index_i);
begin
case (index_i)
0: expected_coefficient = -16'sd4;
1: expected_coefficient = -16'sd17;
3: expected_coefficient = -16'sd3;
8: expected_coefficient = -16'sd146;
24: expected_coefficient = -16'sd15;
40: expected_coefficient = -16'sd5;
56: expected_coefficient = -16'sd1;
default: expected_coefficient = 16'sd0;
endcase
end
endfunction
initial begin
clk = 1'b0;
rst_n = 1'b0;
start = 1'b0;
inverse = 1'b0;
block_in = '0;
result_ready = 1'b0;
repeat (3) @(negedge clk);
rst_n = 1'b1;
for (int index = 0; index < 64; index++) begin
block_in[index*24+:24] = 24'(index - 32);
end
run_transform(1'b0);
for (int index = 0; index < 64; index++) begin
if ($signed(block_out[index*24+:24]) != expected_coefficient(index)) begin
$fatal(1, "forward transform mismatch at %0d: %0d", index, $signed(
block_out[index*24+:24]));
end
end
block_in = block_out;
result_ready = 1'b1;
@(negedge clk);
result_ready = 1'b0;
run_transform(1'b1);
for (int index = 0; index < 64; index++) begin
if (($signed(
block_out[index*24+:24]
) < (index - 33)) || ($signed(
block_out[index*24+:24]
) > (index - 31))) begin
$fatal(1, "inverse transform mismatch at %0d: %0d", index, $signed(
block_out[index*24+:24]));
end
end
result_ready = 1'b1;
@(negedge clk);
$display("JPEG transform tests passed");
$finish;
end
endmodule
+2 -1
View File
@@ -42,6 +42,7 @@ module soc_irq_topology_tb;
19: expect_irq(32'd1 << 24);
20: expect_irq(32'd1 << 25);
21: expect_irq(32'd1 << 26);
22: expect_irq(32'd1 << 30);
default: expect_irq(32'd1 << (bit_index + 7));
endcase
end
@@ -69,7 +70,7 @@ module soc_irq_topology_tb;
$fatal(1, "SDIO core IRQ bits are not low when sources are low: IRQ10=%b IRQ21=%b",
s_irq[10], s_irq[21]);
end
if ((s_irq[31:30] !== '0) || (s_irq[19:18] !== '0)) begin
if ((s_irq[31] !== 1'b0) || (s_irq[19:18] !== '0)) begin
$fatal(1, "unallocated core IRQ bits are not low: %h", s_irq);
end