Files
retroSoC/rtl/ip/multimedia/dvp_reg.sv
T
maksyuki c0f04067b2 docs: add licensing information and attribution comments to various RTL and test files
- Updated multiple RTL files in the  and  directories to include licensing information under the Mulan PSL v2.
- Added attribution comments for contributions from ETH Zurich and University of Bologna in .
- Enhanced test files in the  directory with licensing details and ensured compliance with the Solderpad Hardware License, Version 0.51.
- Introduced a new test in  to verify the preservation of attribution for PULP HyperBus sources.
2026-08-24 10:02:48 +08:00

343 lines
12 KiB
Systemverilog

// Copyright (c) 2026 Yuchi Miao <miaoyuchi@ict.ac.cn>
// retroSoC is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
`include "dvp_define.svh"
module dvp_reg (
// verilog_format: off -- preserve reviewed column alignment
input logic clk_i,
input logic rst_n_i,
apb4_if.slave apb4,
input logic active_i,
input logic fifo_empty_i,
input logic fifo_full_i,
input logic [31:0] rx_data_i,
input logic frame_start_i,
input logic line_done_i,
input logic frame_done_i,
input logic error_event_i,
input logic [ 5:0] error_flags_i,
input logic [127:0] frame_stats_i,
input logic frame_stats_valid_i,
output logic [127:0] cfg_o,
output logic cfg_valid_o,
output logic cmd_abort_o,
output logic cmd_flush_o,
output logic cmd_valid_o,
input logic cmd_ready_i,
output logic rx_pop_o,
input logic cfg_ready_i,
output logic stream_enable_o,
output logic irq_o
// verilog_format: on
);
localparam logic [31:0] IpVersion = 32'h0002_0000;
localparam logic [31:0] Capability = 32'h0000_3F7F;
localparam logic [31:0] CtrlMask = 32'h0000_0007;
localparam logic [31:0] StreamMask = 32'h0000_0001;
localparam logic [31:0] FormatMask = 32'h0000_000F;
localparam logic [31:0] SyncMask = 32'h0000_0007;
logic s_req;
logic s_write;
logic s_accept;
logic [11:0] s_offset;
logic s_aligned;
logic s_access_err;
logic s_ready_q;
logic s_resp_err_d, s_resp_err_q;
logic [31:0] s_rdata_d, s_rdata_q;
logic [31:0] s_ctrl_d, s_ctrl_q;
logic [31:0] s_stream_d, s_stream_q;
logic [31:0] s_format_d, s_format_q;
logic [31:0] s_sync_d, s_sync_q;
logic [31:0] s_frame_size_d, s_frame_size_q;
logic [31:0] s_crop_start_d, s_crop_start_q;
logic [31:0] s_crop_size_d, s_crop_size_q;
logic [5:0] s_err_q;
logic [6:0] s_intr_stat_q;
logic [6:0] s_intr_en_q;
logic [31:0] s_frm_cnt_q, s_line_cnt_q;
logic [31:0] s_pixel_cnt_q, s_word_cnt_q, s_drop_cnt_q;
logic [31:0] s_cfg_seq_d, s_cfg_seq_q;
logic [31:0] s_cfg_sent_q;
logic [ 1:0] s_cmd_q;
function automatic logic [31:0] merge_wstrb(
input logic [31:0] current_i, input logic [31:0] value_i, input logic [3:0] strobe_i);
logic [31:0] merged;
begin
merged = current_i;
for (int index = 0; index < 4; index++) begin
if (strobe_i[index]) merged[index*8+:8] = value_i[index*8+:8];
end
return merged;
end
endfunction
assign s_req = apb4.psel && apb4.penable && !s_ready_q;
assign s_write = |apb4.pstrb;
assign s_accept = s_req;
assign s_offset = apb4.paddr[11:0];
assign s_aligned = apb4.paddr[1:0] == 2'b00;
assign apb4.pready = s_ready_q;
assign apb4.prdata = s_rdata_q;
assign apb4.pslverr = s_resp_err_q;
assign stream_enable_o = s_stream_q[`APB4_DVP__STREAM_ENABLE];
always_comb begin
cfg_o = '0;
cfg_o[15:0] = s_frame_size_q[15:0];
cfg_o[31:16] = s_frame_size_q[31:16];
cfg_o[47:32] = s_crop_start_q[15:0];
cfg_o[63:48] = s_crop_start_q[31:16];
cfg_o[79:64] = s_crop_size_q[15:0];
cfg_o[95:80] = s_crop_size_q[31:16];
cfg_o[96] = s_ctrl_q[`APB4_DVP__CTRL_ENABLE];
cfg_o[97] = s_ctrl_q[`APB4_DVP__CTRL_SNAPSHOT];
cfg_o[98] = s_ctrl_q[`APB4_DVP__CTRL_CROP_ENABLE];
cfg_o[99] = s_stream_q[`APB4_DVP__STREAM_ENABLE];
cfg_o[103:100] = s_format_q[3:0];
cfg_o[106:104] = s_sync_q[2:0];
end
assign cfg_valid_o = s_cfg_seq_q != s_cfg_sent_q;
assign rx_pop_o = s_accept && !s_write && !stream_enable_o && (s_offset == `APB4_DVP_RXDATA);
assign irq_o = |(s_intr_stat_q & s_intr_en_q);
always_comb begin
s_access_err = !s_aligned;
s_rdata_d = 32'd0;
if (s_aligned) begin
unique case (s_offset)
`APB4_DVP_CTRL: s_rdata_d = s_ctrl_q;
`APB4_DVP_RXDATA: s_rdata_d = rx_data_i;
`APB4_DVP_STATUS:
s_rdata_d = {
26'd0, s_err_q != 0, stream_enable_o, !fifo_full_i, fifo_empty_i, active_i, s_ctrl_q[0]
};
`APB4_DVP_STREAM_CTRL: s_rdata_d = s_stream_q;
`APB4_DVP_FORMAT: s_rdata_d = s_format_q;
`APB4_DVP_SYNC_CFG: s_rdata_d = s_sync_q;
`APB4_DVP_FRAME_SIZE: s_rdata_d = s_frame_size_q;
`APB4_DVP_CROP_START: s_rdata_d = s_crop_start_q;
`APB4_DVP_CROP_SIZE: s_rdata_d = s_crop_size_q;
`APB4_DVP_FRAME_COUNT: s_rdata_d = s_frm_cnt_q;
`APB4_DVP_LINE_COUNT: s_rdata_d = s_line_cnt_q;
`APB4_DVP_PIXEL_COUNT: s_rdata_d = s_pixel_cnt_q;
`APB4_DVP_WORD_COUNT: s_rdata_d = s_word_cnt_q;
`APB4_DVP_DROP_COUNT: s_rdata_d = s_drop_cnt_q;
`APB4_DVP_ERROR_STATUS: s_rdata_d = {26'd0, s_err_q};
`APB4_DVP_INTR_STATE: s_rdata_d = {25'd0, s_intr_stat_q};
`APB4_DVP_INTR_ENABLE: s_rdata_d = {25'd0, s_intr_en_q};
`APB4_DVP_INTR_STATUS: s_rdata_d = {25'd0, s_intr_stat_q & s_intr_en_q};
`APB4_DVP_INTR_TEST: s_access_err = !s_write;
`APB4_DVP_IP_VERSION: s_rdata_d = IpVersion;
`APB4_DVP_CAPABILITY: s_rdata_d = Capability;
`APB4_DVP_COMMAND: s_access_err = !s_write;
default: s_access_err = 1'b1;
endcase
end
if (s_accept && s_write && active_i &&
((s_offset == `APB4_DVP_CTRL) || (s_offset == `APB4_DVP_STREAM_CTRL) ||
(s_offset == `APB4_DVP_FORMAT) || (s_offset == `APB4_DVP_SYNC_CFG) ||
(s_offset == `APB4_DVP_FRAME_SIZE) || (s_offset == `APB4_DVP_CROP_START) ||
(s_offset == `APB4_DVP_CROP_SIZE))) begin
s_access_err = 1'b1;
end
if (s_accept && s_write && (s_offset == `APB4_DVP_FRAME_SIZE) &&
((apb4.pwdata[15:0] == 0) || (apb4.pwdata[31:16] == 0))) begin
s_access_err = 1'b1;
end
s_resp_err_d = s_accept && s_access_err;
end
assign cmd_valid_o = |s_cmd_q;
assign cmd_abort_o = s_cmd_q[`APB4_DVP__COMMAND_ABORT];
assign cmd_flush_o = s_cmd_q[`APB4_DVP__COMMAND_FLUSH];
always_comb begin
s_ctrl_d = s_ctrl_q;
s_stream_d = s_stream_q;
s_format_d = s_format_q;
s_sync_d = s_sync_q;
s_frame_size_d = s_frame_size_q;
s_crop_start_d = s_crop_start_q;
s_crop_size_d = s_crop_size_q;
s_cfg_seq_d = s_cfg_seq_q;
if (s_accept && s_write && !s_access_err) begin
unique case (s_offset)
`APB4_DVP_CTRL: begin
s_ctrl_d = merge_wstrb(s_ctrl_q, apb4.pwdata, apb4.pstrb) & CtrlMask;
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
`APB4_DVP_STREAM_CTRL: begin
s_stream_d = merge_wstrb(s_stream_q, apb4.pwdata, apb4.pstrb) & StreamMask;
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
`APB4_DVP_FORMAT: begin
s_format_d = merge_wstrb(s_format_q, apb4.pwdata, apb4.pstrb) & FormatMask;
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
`APB4_DVP_SYNC_CFG: begin
s_sync_d = merge_wstrb(s_sync_q, apb4.pwdata, apb4.pstrb) & SyncMask;
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
`APB4_DVP_FRAME_SIZE: begin
s_frame_size_d = merge_wstrb(s_frame_size_q, apb4.pwdata, apb4.pstrb);
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
`APB4_DVP_CROP_START: begin
s_crop_start_d = merge_wstrb(s_crop_start_q, apb4.pwdata, apb4.pstrb);
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
`APB4_DVP_CROP_SIZE: begin
s_crop_size_d = merge_wstrb(s_crop_size_q, apb4.pwdata, apb4.pstrb);
s_cfg_seq_d = s_cfg_seq_q + 1'b1;
end
default: begin
end
endcase
end
end
dffr #(
.DATA_WIDTH(32)
) u_ctrl_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_ctrl_d),
.dat_o (s_ctrl_q)
);
dffr #(
.DATA_WIDTH(32)
) u_stream_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_stream_d),
.dat_o (s_stream_q)
);
dffr #(
.DATA_WIDTH(32)
) u_format_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_format_d),
.dat_o (s_format_q)
);
dffr #(
.DATA_WIDTH(32)
) u_sync_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_sync_d),
.dat_o (s_sync_q)
);
dffr #(
.DATA_WIDTH(32)
) u_frame_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_frame_size_d),
.dat_o (s_frame_size_q)
);
dffr #(
.DATA_WIDTH(32)
) u_crop_start_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crop_start_d),
.dat_o (s_crop_start_q)
);
dffr #(
.DATA_WIDTH(32)
) u_crop_size_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_crop_size_d),
.dat_o (s_crop_size_q)
);
dffr #(
.DATA_WIDTH(32)
) u_config_seq_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (s_cfg_seq_d),
.dat_o (s_cfg_seq_q)
);
always_ff @(posedge clk_i or negedge rst_n_i) begin
if (!rst_n_i) begin
s_cfg_sent_q <= '0;
s_cmd_q <= '0;
s_err_q <= '0;
s_intr_stat_q <= '0;
s_intr_en_q <= '0;
s_frm_cnt_q <= '0;
s_line_cnt_q <= '0;
s_pixel_cnt_q <= '0;
s_word_cnt_q <= '0;
s_drop_cnt_q <= '0;
end else begin
if (cfg_valid_o && cfg_ready_i) s_cfg_sent_q <= s_cfg_seq_q;
if (cmd_valid_o && cmd_ready_i) s_cmd_q <= '0;
if (s_accept && s_write && !s_access_err && (s_offset == `APB4_DVP_COMMAND)) begin
s_cmd_q <= s_cmd_q | apb4.pwdata[1:0];
end
s_err_q <= (s_err_q | error_flags_i) &
~((s_accept && s_write && (s_offset == `APB4_DVP_ERROR_STATUS))
? apb4.pwdata[5:0]
: 6'd0);
if (frame_start_i) s_intr_stat_q[`APB4_DVP__INTR_FRAME_START] <= 1'b1;
if (line_done_i) s_intr_stat_q[`APB4_DVP__INTR_LINE_DONE] <= 1'b1;
if (frame_done_i) s_intr_stat_q[`APB4_DVP__INTR_FRAME_DONE] <= 1'b1;
if (error_event_i) s_intr_stat_q[`APB4_DVP__INTR_OVERFLOW] <= 1'b1;
if (s_accept && s_write && (s_offset == `APB4_DVP_INTR_STATE))
s_intr_stat_q <= s_intr_stat_q & ~apb4.pwdata[6:0];
if (s_accept && s_write && (s_offset == `APB4_DVP_INTR_ENABLE))
s_intr_en_q <= apb4.pwdata[6:0];
if (s_accept && s_write && (s_offset == `APB4_DVP_INTR_TEST))
s_intr_stat_q <= s_intr_stat_q | apb4.pwdata[6:0];
if (frame_stats_valid_i) begin
s_frm_cnt_q <= frame_stats_i[31:0];
s_line_cnt_q <= {16'd0, frame_stats_i[47:32]};
s_pixel_cnt_q <= frame_stats_i[79:48];
s_word_cnt_q <= {16'd0, frame_stats_i[95:80]};
s_drop_cnt_q <= frame_stats_i[127:96];
end
end
end
dffr #(
.DATA_WIDTH(1)
) u_ready_dffr (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.dat_i (apb4.psel && apb4.penable && !s_ready_q),
.dat_o (s_ready_q)
);
dffer #(
.DATA_WIDTH(32)
) u_rdata_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_accept && !s_write),
.dat_i (s_rdata_d),
.dat_o (s_rdata_q)
);
dffer #(
.DATA_WIDTH(1)
) u_resp_err_dffer (
.clk_i (clk_i),
.rst_n_i(rst_n_i),
.en_i (s_accept),
.dat_i (s_resp_err_d),
.dat_o (s_resp_err_q)
);
endmodule