mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-06 14:55:14 +00:00
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:
75 files changed
+15048
-58
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user