//////////////////////////////////////////////////////////////////////////////// // // Filename: axivdisplay // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: Reads from memory for the purposes of serving a video frame // {{{ // buffer. The result of this core is an AXI stream having the // word size of the memory interface in the first place. TLAST is set // based upon the end of the frame, and TUSER based upon the end of the // line. An option exists to use Xilinx's encoding instead where TLAST // is the end of the line and TUSER is the first pixel word of the frame. // // This core is in many ways similar to the AXI MM2S core, but with some // key differences: The AXI MM2S core generates a single transfer. This // core generates a repeating set of transfers--one per line, repeated // per frame. // // To insure high bandwidth, data is first copied into a FIFO of twice // the width of the longest burst. // // }}} // Registers: // {{{ // 0: FBUF_CONTROL and status // bit 0: START(1)/STOP(0) // Command the core to start by writing a '1' to this bit. It // will then remain busy/active until either you tell it to halt, // or an error occurrs. // bit 1: BUSY // bit 2: ERR // If the core receives a bus error, it assumes it has been // inappropriately set up, sets this error bit and then halts. // It will not start again until this bit is cleared. Only a // write to the control register with the ERR bit set will clear // it. (Don't do this unless you know what caused it to halt ...) // bit 3: DIRTY // If you update core parameters while it is running, the busy // bit will be set. This bit is an indication that the current // configuration doesn't necessarily match the one you are reading // out. To clear DIRTY, deactivate the core, wait for it to be // no longer busy, and then start it again. This will also start // it under the new configuration so the two match. // // 2: FBUF_LINESTEP // Controls the distance from one line to the next. This is the // value added to the address of the beginning of the line to get // to the beginning of the next line. This should nominally be // equal to the number of bytes per line, although it doesn't // need to be. // // Any attempt to set this value to zero will simply copy the // number of data bytes per line into this value. // // 4: FBUF_LINEBYTES // This is the number of data bytes necessary to capture all of // the video data in a line. This value must be more than zero // in order to activate the core. // // At present, this core can only handle a number of bytes aligned // with the word size of the bus. // // 6: FBUF_NUMLINES // The number of lines of active video data in a frame. This // number must be greater than zero in order to activate and // operate the core. // // 8: FBUF_ADDRESS // The is the first address of video data in memory. Each frame // will start reading from this address. // // 12: (reserved for the upper FBUF_ADDRESS) // // BUGS: // 1. Memory reads are currently allowed to wrap around the end of memory. // I'm not (yet) sure if this is a good thing or a bad thing. I mean, its // a great thing for small dedicated memories designated for this purpose // alone, but perhaps a bad thing if the memory space is shared with // peripherals as well as a CPU. // // 2. I'd still like to add an option to handle memory addresses that // aren't aligned. Until that is done, the means of interacting with the // core will change from one implementation to another. // // 3. If the configuration changes mid-write, but without de-activating // the core and waiting for it to come to idle, the synchronization flags // might out-of-sync with the pixels. To resolve, deactivate the core and // let it come to whenever changing configuration parameters. // }}} // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // //////////////////////////////////////////////////////////////////////////////// // }}} // Copyright (C) 2020-2024, Gisselquist Technology, LLC // {{{ // // This file is part of the WB2AXIP project. // // The WB2AXIP project contains free software and gateware, licensed under the // Apache License, Version 2.0 (the "License"). You may not use this project, // or this file, except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. // //////////////////////////////////////////////////////////////////////////////// // // `default_nettype none // }}} module axivdisplay #( // {{{ parameter C_AXI_ADDR_WIDTH = 32, parameter C_AXI_DATA_WIDTH = 32, parameter C_AXI_ID_WIDTH = 1, // // We support five 32-bit AXI-lite registers, requiring 5-bits // of AXI-lite addressing localparam C_AXIL_ADDR_WIDTH = 4, localparam C_AXIL_DATA_WIDTH = 32, // // The bottom ADDRLSB bits of any AXI address are subword bits localparam ADDRLSB = $clog2(C_AXI_DATA_WIDTH)-3, localparam AXILLSB = $clog2(C_AXIL_DATA_WIDTH)-3, // // OPT_UNALIGNED: Allow unaligned accesses, address requests // and sizes which may or may not match the underlying data // width. If set, the core will quietly align these requests. // parameter [0:0] OPT_UNALIGNED = 1'b0, // // OPT_LGMAXBURST parameter OPT_LGMAXBURST = 8, // parameter [0:0] DEF_ACTIVE_ON_RESET = 0, parameter [15:0] DEF_LINES_PER_FRAME = 1024, parameter [16-ADDRLSB-1:0] DEF_WORDS_PER_LINE = (1280 * 32)/C_AXI_DATA_WIDTH, // // DEF_FRAMEADDR: the default AXI address of the frame buffer // containing video memory. Unless OPT_UNALIGNED is set, this // should be aligned so that DEF_FRAMEADDR[ADDRLSB-1:0] == 0. parameter [C_AXI_ADDR_WIDTH-1:0] DEF_FRAMEADDR = 0, // // The (log-based two of the) size of the FIFO in words. // I like to set this to the size of two AXI bursts, so that // while one is being read out the second can be read in. Can // also be set larger if desired. parameter LGFIFO = OPT_LGMAXBURST+1, // // AXI_ID is the ID we will use for all of our AXI transactions parameter AXI_ID = 0, // // OPT_TUSER_IS_SOF. Xilinx and I chose different stream // encodings. I encode TLAST== VLAST and // TUSER == (optional) HLAST. Xilinx chose TLAST == HLAST // and TUSER == SOF(start of frame). Set OPT_TUSER_IS_SOF to // use Xilinx's encoding. parameter [0:0] OPT_TUSER_IS_SOF = 0 // }}} ) ( // {{{ input wire S_AXI_ACLK, input wire S_AXI_ARESETN, // // The video stream interface // {{{ output wire M_AXIS_TVALID, input wire M_AXIS_TREADY, output wire [C_AXI_DATA_WIDTH-1:0] M_AXIS_TDATA, output wire /* VLAST or HLAST */ M_AXIS_TLAST, output wire /* HLAST or SOF */ M_AXIS_TUSER, // }}} // // The control interface // {{{ input wire S_AXIL_AWVALID, output wire S_AXIL_AWREADY, input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_AWADDR, input wire [2:0] S_AXIL_AWPROT, // input wire S_AXIL_WVALID, output wire S_AXIL_WREADY, input wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_WDATA, input wire [C_AXIL_DATA_WIDTH/8-1:0] S_AXIL_WSTRB, // output wire S_AXIL_BVALID, input wire S_AXIL_BREADY, output wire [1:0] S_AXIL_BRESP, // input wire S_AXIL_ARVALID, output wire S_AXIL_ARREADY, input wire [C_AXIL_ADDR_WIDTH-1:0] S_AXIL_ARADDR, input wire [2:0] S_AXIL_ARPROT, // output wire S_AXIL_RVALID, input wire S_AXIL_RREADY, output wire [C_AXIL_DATA_WIDTH-1:0] S_AXIL_RDATA, output wire [1:0] S_AXIL_RRESP, // }}} // // // The AXI (full) read interface // {{{ output wire M_AXI_ARVALID, input wire M_AXI_ARREADY, output wire [C_AXI_ID_WIDTH-1:0] M_AXI_ARID, output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_ARADDR, output wire [7:0] M_AXI_ARLEN, output wire [2:0] M_AXI_ARSIZE, output wire [1:0] M_AXI_ARBURST, output wire M_AXI_ARLOCK, output wire [3:0] M_AXI_ARCACHE, output wire [2:0] M_AXI_ARPROT, output wire [3:0] M_AXI_ARQOS, // input wire M_AXI_RVALID, output wire M_AXI_RREADY, input wire [C_AXI_DATA_WIDTH-1:0] M_AXI_RDATA, input wire M_AXI_RLAST, input wire [C_AXI_ID_WIDTH-1:0] M_AXI_RID, input wire [1:0] M_AXI_RRESP // }}} // }}} ); // Core logic implementation // {{{ // Local parameter declarations // {{{ localparam [1:0] FBUF_CONTROL = 2'b00, FBUF_FRAMEINFO = 2'b01, FBUF_ADDRLO = 2'b10, FBUF_ADDRHI = 2'b11; localparam CBIT_ACTIVE = 0, CBIT_BUSY = 1, CBIT_ERR = 2, CBIT_DIRTY = 3; localparam TMPLGMAXBURST=(LGFIFO-1 > OPT_LGMAXBURST) ? OPT_LGMAXBURST : LGFIFO-1; localparam LGMAXBURST = (TMPLGMAXBURST+ADDRLSB > 12) ? (12-ADDRLSB) : TMPLGMAXBURST; // }}} wire i_clk = S_AXI_ACLK; wire i_reset = !S_AXI_ARESETN; // Signal declarations // {{{ reg soft_reset, r_err, r_stopped; reg cfg_active, cfg_zero_length, cfg_dirty; reg [C_AXI_ADDR_WIDTH-1:0] cfg_frame_addr; reg [15:0] cfg_frame_lines, cfg_line_step; reg [16-ADDRLSB-1:0] cfg_line_words; // FIFO signals wire reset_fifo, write_to_fifo, read_from_fifo; wire [C_AXI_DATA_WIDTH-1:0] write_data; wire [LGFIFO:0] fifo_fill; wire fifo_full, fifo_empty; wire awskd_valid, axil_write_ready; wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] awskd_addr; // wire wskd_valid; wire [C_AXIL_DATA_WIDTH-1:0] wskd_data; wire [C_AXIL_DATA_WIDTH/8-1:0] wskd_strb; reg axil_bvalid; // wire arskd_valid, axil_read_ready; wire [C_AXIL_ADDR_WIDTH-AXILLSB-1:0] arskd_addr; reg [C_AXIL_DATA_WIDTH-1:0] axil_read_data; reg axil_read_valid; reg [C_AXIL_DATA_WIDTH-1:0] w_status_word; reg [2*C_AXIL_DATA_WIDTH-1:0] wide_address, new_wideaddr; wire [C_AXIL_DATA_WIDTH-1:0] new_cmdaddrlo, new_cmdaddrhi; reg [C_AXIL_DATA_WIDTH-1:0] wide_config; wire [C_AXIL_DATA_WIDTH-1:0] new_config; // reg partial_burst_requested; // reg [LGMAXBURST-1:0] addralign; // reg [LGFIFO:0] rd_uncommitted; // reg [LGMAXBURST:0] initial_burstlen; // reg [LGLENWA-1:0] rd_reads_remaining; // reg rd_none_remaining, // rd_last_remaining; // wire realign_last_valid; reg axi_arvalid, lag_start, phantom_start, start_burst, ar_none_outstanding; reg [C_AXI_ADDR_WIDTH-1:0] axi_araddr; reg [LGMAXBURST:0] max_burst; reg [7:0] axi_arlen; reg [15:0] ar_bursts_outstanding; // wire vlast, hlast; reg [15:0] r_frame_lines, r_line_step; reg [16-ADDRLSB-1:0] r_line_words; reg req_hlast, req_vlast; reg [15:0] req_nlines; reg [16-ADDRLSB-1:0] req_line_words; reg [C_AXI_ADDR_WIDTH:0] req_addr, req_line_addr; // reg rd_hlast, rd_vlast; reg [15:0] rd_lines; reg [16-ADDRLSB-1:0] rd_line_beats; wire no_fifo_space_available; // reg [LGMAXBURST-1:0] till_boundary; reg [LGFIFO:0] fifo_space_available; `ifdef FORMAL reg [C_AXI_ADDR_WIDTH:0] f_rd_line_addr, f_rd_addr; `endif wire M_AXIS_VLAST, M_AXIS_HLAST; // }}} //////////////////////////////////////////////////////////////////////// // // AXI-lite signaling // //////////////////////////////////////////////////////////////////////// // // This is mostly the skidbuffer logic, and handling of the VALID // and READY signals for the AXI-lite control logic in the next // section. // {{{ // // Write signaling // // {{{ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB)) axilawskid(// .i_clk(S_AXI_ACLK), .i_reset(i_reset), .i_valid(S_AXIL_AWVALID), .o_ready(S_AXIL_AWREADY), .i_data(S_AXIL_AWADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]), .o_valid(awskd_valid), .i_ready(axil_write_ready), .o_data(awskd_addr)); skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_DATA_WIDTH+C_AXIL_DATA_WIDTH/8)) axilwskid(// .i_clk(S_AXI_ACLK), .i_reset(i_reset), .i_valid(S_AXIL_WVALID), .o_ready(S_AXIL_WREADY), .i_data({ S_AXIL_WDATA, S_AXIL_WSTRB }), .o_valid(wskd_valid), .i_ready(axil_write_ready), .o_data({ wskd_data, wskd_strb })); assign axil_write_ready = awskd_valid && wskd_valid && (!S_AXIL_BVALID || S_AXIL_BREADY); initial axil_bvalid = 0; always @(posedge i_clk) if (i_reset) axil_bvalid <= 0; else if (axil_write_ready) axil_bvalid <= 1; else if (S_AXIL_BREADY) axil_bvalid <= 0; assign S_AXIL_BVALID = axil_bvalid; assign S_AXIL_BRESP = 2'b00; // }}} // // Read signaling // // {{{ skidbuffer #(.OPT_OUTREG(0), .DW(C_AXIL_ADDR_WIDTH-AXILLSB)) axilarskid(// .i_clk(S_AXI_ACLK), .i_reset(i_reset), .i_valid(S_AXIL_ARVALID), .o_ready(S_AXIL_ARREADY), .i_data(S_AXIL_ARADDR[C_AXIL_ADDR_WIDTH-1:AXILLSB]), .o_valid(arskd_valid), .i_ready(axil_read_ready), .o_data(arskd_addr)); assign axil_read_ready = arskd_valid && (!axil_read_valid || S_AXIL_RREADY); initial axil_read_valid = 1'b0; always @(posedge i_clk) if (i_reset) axil_read_valid <= 1'b0; else if (axil_read_ready) axil_read_valid <= 1'b1; else if (S_AXIL_RREADY) axil_read_valid <= 1'b0; assign S_AXIL_RVALID = axil_read_valid; assign S_AXIL_RDATA = axil_read_data; assign S_AXIL_RRESP = 2'b00; // }}} // }}} //////////////////////////////////////////////////////////////////////// // // AXI-lite controlled logic // //////////////////////////////////////////////////////////////////////// // // {{{ // // soft_reset, r_err // {{{ initial soft_reset = 1; always @(posedge i_clk) if (i_reset) begin soft_reset <= 1; r_err <= 0; end else if (soft_reset) begin if ((axil_write_ready && awskd_addr == FBUF_CONTROL) && wskd_strb[0] && wskd_data[CBIT_ERR]) r_err <= cfg_zero_length; if (cfg_active && !r_err && r_stopped) soft_reset <= 0; end else // if (!soft_reset) begin // Halt on any bus error. We'll require user intervention // to start back up again if (M_AXI_RREADY && M_AXI_RVALID && M_AXI_RRESP[1]) begin soft_reset <= 1; r_err <= 1; end // Halt on any user request if (!cfg_active) soft_reset <= 1; end // }}} // wide_* and new_* write setup registers // {{{ always @(*) begin wide_address = 0; wide_address[C_AXI_ADDR_WIDTH-1:0] = cfg_frame_addr; wide_address[ADDRLSB-1:0] = 0; wide_config = { cfg_frame_lines, cfg_line_words, {(ADDRLSB){1'b0}} }; end assign new_cmdaddrlo = apply_wstrb( wide_address[C_AXIL_DATA_WIDTH-1:0], wskd_data, wskd_strb); generate if (C_AXI_ADDR_WIDTH > 32) begin : GEN_LARGE_AW assign new_cmdaddrhi = apply_wstrb( wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH], wskd_data, wskd_strb); end else begin : GEN_SINGLE_WORD_AW assign new_cmdaddrhi = 0; end endgenerate wire [C_AXIL_DATA_WIDTH-1:0] new_control; assign new_control = apply_wstrb(w_status_word, wskd_data, wskd_strb); assign new_config = apply_wstrb(wide_config, wskd_data, wskd_strb); always @(*) begin new_wideaddr = wide_address; if (awskd_addr == FBUF_ADDRLO) new_wideaddr[C_AXIL_DATA_WIDTH-1:0] = new_cmdaddrlo; if (awskd_addr == FBUF_ADDRHI) new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH] = new_cmdaddrhi; new_wideaddr[ADDRLSB-1:0] = 0; new_wideaddr[2*C_AXIL_DATA_WIDTH-1:C_AXI_ADDR_WIDTH] = 0; end // }}} // Configuration registers (Write) // {{{ initial cfg_active = 0; initial cfg_frame_addr = DEF_FRAMEADDR; initial cfg_frame_addr[ADDRLSB-1:0] = 0; initial cfg_line_words = DEF_WORDS_PER_LINE; initial cfg_frame_lines = DEF_LINES_PER_FRAME; initial cfg_zero_length = (DEF_WORDS_PER_LINE == 0) ||(DEF_LINES_PER_FRAME == 0); always @(posedge i_clk) if (i_reset) begin cfg_active <= DEF_ACTIVE_ON_RESET; cfg_frame_addr <= DEF_FRAMEADDR; cfg_line_words <= DEF_WORDS_PER_LINE; cfg_line_step <= { DEF_WORDS_PER_LINE, {(ADDRLSB){1'b0}} }; cfg_frame_lines <= DEF_LINES_PER_FRAME; cfg_zero_length <= (DEF_WORDS_PER_LINE==0) ||(DEF_LINES_PER_FRAME == 0); cfg_dirty <= 0; end else begin if (r_stopped) cfg_dirty <= 0; if (cfg_active && req_hlast && req_vlast && phantom_start) cfg_dirty <= 0; if (M_AXI_RREADY && M_AXI_RVALID && M_AXI_RRESP[1]) cfg_active <= 0; if (axil_write_ready) case(awskd_addr) FBUF_CONTROL: begin if (wskd_strb[0]) cfg_active <= wskd_data[CBIT_ACTIVE] && (!r_err || wskd_data[CBIT_ERR]) && (!cfg_zero_length); if (new_control[31:16] == 0) begin cfg_line_step <= 0; cfg_line_step[16-1:ADDRLSB] <= cfg_line_words; // Verilator lint_off WIDTH cfg_dirty <= (cfg_line_step != (cfg_line_words << ADDRLSB)); // Verilator lint_on WIDTH end else begin cfg_line_step <= new_control[31:16]; cfg_dirty <= (cfg_line_step != new_control[31:16]); end end FBUF_FRAMEINFO: begin { cfg_frame_lines, cfg_line_words } <= new_config[C_AXI_DATA_WIDTH-1:ADDRLSB]; cfg_zero_length <= (new_config[31:16] == 0) ||(new_config[15:ADDRLSB] == 0); if ((new_config[31:16] == 0) ||(new_config[15:ADDRLSB] == 0)) cfg_active <= 0; cfg_dirty <= 1; end FBUF_ADDRLO, FBUF_ADDRHI: begin cfg_frame_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0]; cfg_dirty <= 1; end default: begin end endcase end // }}} // AXI-Lite read register data // {{{ always @(*) begin w_status_word = 0; w_status_word[31:16] = cfg_line_step; w_status_word[CBIT_DIRTY] = cfg_dirty; w_status_word[CBIT_ERR] = r_err; w_status_word[CBIT_BUSY] = !soft_reset; w_status_word[CBIT_ACTIVE] = cfg_active || (!soft_reset || !r_stopped); end always @(posedge i_clk) if (!axil_read_valid || S_AXIL_RREADY) begin axil_read_data <= 0; case(arskd_addr) FBUF_CONTROL: axil_read_data <= w_status_word; FBUF_FRAMEINFO: axil_read_data <= { cfg_frame_lines, cfg_line_words, {(ADDRLSB){1'b0}} }; FBUF_ADDRLO: axil_read_data <= wide_address[C_AXIL_DATA_WIDTH-1:0]; FBUF_ADDRHI: axil_read_data <= wide_address[2*C_AXIL_DATA_WIDTH-1:C_AXIL_DATA_WIDTH]; default axil_read_data <= 0; endcase end // }}} // apply_wstrb function for applying wstrbs to register words // {{{ function [C_AXIL_DATA_WIDTH-1:0] apply_wstrb; input [C_AXIL_DATA_WIDTH-1:0] prior_data; input [C_AXIL_DATA_WIDTH-1:0] new_data; input [C_AXIL_DATA_WIDTH/8-1:0] wstrb; integer k; for(k=0; k 0) begin : GEN_SPACE_AVAILBLE // Here's where we'll put the actual outgoing FIFO // {{{ initial fifo_space_available = (1<= 3); // }}} end endgenerate // }}} // Switch between TLAST/TUSER encodings if necessary // {{{ generate if (OPT_TUSER_IS_SOF) begin : XILINX_ENCODING reg SOF; initial SOF = 1'b1; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN || r_stopped) SOF <= 1'b1; else if (M_AXIS_TVALID && M_AXIS_TREADY) SOF <= M_AXIS_VLAST; assign M_AXIS_TLAST = M_AXIS_HLAST; assign M_AXIS_TUSER = SOF; end else begin : VLAST_EQUALS_TLAST assign M_AXIS_TLAST = M_AXIS_VLAST; assign M_AXIS_TUSER = M_AXIS_HLAST; end endgenerate // }}} // }}} //////////////////////////////////////////////////////////////////////// // // Outoing frame address counting // //////////////////////////////////////////////////////////////////////// // // // {{{ always @(posedge i_clk) if (i_reset || r_stopped || (phantom_start && req_hlast && req_vlast)) begin r_frame_lines <= cfg_frame_lines; r_line_words <= cfg_line_words; r_line_step <= { {(ADDRLSB){1'b0}}, cfg_line_step[15:ADDRLSB] }; end initial req_addr = 0; initial req_line_addr = 0; always @(posedge i_clk) if (i_reset || r_stopped) begin req_addr <= { 1'b0, cfg_frame_addr }; req_line_addr <= { 1'b0, cfg_frame_addr }; req_line_words <= cfg_line_words; end else if (phantom_start) begin if (req_hlast && req_vlast) begin req_addr <= { 1'b0, cfg_frame_addr }; req_line_addr <= { 1'b0, cfg_frame_addr }; req_line_words <= cfg_line_words; end else if (req_hlast) begin // verilator lint_off WIDTH req_addr <= req_line_addr + (r_line_step << M_AXI_ARSIZE); req_line_addr <= req_line_addr + (r_line_step << M_AXI_ARSIZE); // verilator lint_on WIDTH req_line_words <= r_line_words; end else begin // verilator lint_off WIDTH req_addr <= req_addr + (1<<(LGMAXBURST+ADDRLSB)); req_line_words <= req_line_words - (M_AXI_ARLEN+1); // verilator lint_on WIDTH req_addr[LGMAXBURST+ADDRLSB-1:0] <= 0; end end always @(posedge i_clk) if (i_reset || r_stopped) begin req_nlines <= cfg_frame_lines-1; req_vlast <= (cfg_frame_lines <= 1); end else if (phantom_start && req_hlast) begin if (req_vlast) begin req_nlines <= cfg_frame_lines-1; req_vlast <= (cfg_frame_lines <= 1); end else begin req_nlines <= req_nlines - 1; req_vlast <= (req_nlines <= 1); end end `ifdef FORMAL always @(*) if (!r_stopped) begin assert(req_vlast == (req_nlines == 0)); assert(req_addr >= { 1'b0, cfg_frame_addr }); assert(req_line_addr >= { 1'b0, cfg_frame_addr }); assert(req_line_addr <= req_addr); end always @(*) if (cfg_active) begin assert(cfg_frame_lines != 0); assert(cfg_line_words != 0); end always @(*) if (!r_stopped) begin assert(r_frame_lines != 0); assert(r_line_words != 0); end `endif // }}} //////////////////////////////////////////////////////////////////////// // // Incoming frame address counting // //////////////////////////////////////////////////////////////////////// // // // {{{ always @(posedge i_clk) if (i_reset || r_stopped) begin rd_line_beats <= cfg_line_words-1; rd_hlast <= (cfg_line_words == 1); end else if (M_AXI_RVALID && M_AXI_RREADY) begin if (rd_hlast) begin rd_line_beats <= r_line_words - 1; rd_hlast <= (r_line_words <= 1); end else begin rd_line_beats <= rd_line_beats - 1; rd_hlast <= (rd_line_beats <= 1); end end always @(posedge i_clk) if (i_reset || r_stopped) begin rd_lines <= cfg_frame_lines-1; rd_vlast <= (cfg_frame_lines == 1); end else if (M_AXI_RVALID && M_AXI_RREADY && rd_hlast) begin if (vlast) begin rd_lines <= r_frame_lines - 1; rd_vlast <= (r_frame_lines <= 1); end else begin rd_lines <= rd_lines - 1; rd_vlast <= (rd_lines <= 1); end end `ifdef FORMAL always @(posedge i_clk) if (i_reset || r_stopped) begin f_rd_addr <= { 1'b0, cfg_frame_addr }; f_rd_line_addr <= { 1'b0, cfg_frame_addr }; end else if (M_AXI_RVALID && M_AXI_RREADY) begin if (rd_vlast && rd_hlast) begin f_rd_addr <= cfg_frame_addr; f_rd_line_addr <= cfg_frame_addr; end else if (rd_hlast) begin f_rd_addr <= f_rd_line_addr + (r_line_step << M_AXI_ARSIZE); f_rd_line_addr <= f_rd_line_addr + (r_line_step << M_AXI_ARSIZE); end else begin f_rd_addr <= f_rd_addr + (1<= (1< 0 && max_burst <= till_boundary) axi_arlen <= max_burst-1; // Verilator lint_on WIDTH else axi_arlen <= till_boundary; end // }}} // req_hlast // {{{ always @(posedge i_clk) if (lag_start || start_burst) begin req_hlast <= 1; // Verilator lint_off WIDTH if (req_line_words > till_boundary+1) req_hlast <= 0; if (req_line_words > max_burst) req_hlast <= 0; // Verilator lint_on WIDTH end `ifdef FORMAL always @(*) if (phantom_start) begin if (req_hlast) begin assert(axi_arlen+1 == req_line_words); end else assert(axi_arlen+1 < req_line_words); end else if (!soft_reset && !r_stopped && !lag_start) begin if (max_burst != req_line_words) begin assert(!req_hlast); end else if (!req_hlast && !M_AXI_ARVALID) assert(axi_arlen < max_burst); assert(max_burst > 0); if (req_line_words > (1<= cfg_line_words) &&(cfg_line_words > 5) &&(cfg_frame_lines > 4); else begin if ($changed(cfg_line_step)) cvr_full_size <= 0; if ($changed(cfg_line_words)) cvr_full_size <= 0; if ($changed(cfg_frame_lines)) cvr_full_size <= 0; if ($changed(cfg_frame_addr)) cvr_full_size <= 0; end always @(posedge i_clk) if (r_stopped || !cvr_full_size) cvr_full_frame <= 0; else if (!cvr_full_frame) cvr_full_frame <= rd_vlast && rd_hlast && M_AXI_RVALID && M_AXI_RREADY; always @(*) cover(!soft_reset); always @(*) cover(start_burst); always @(*) cover(M_AXI_ARVALID && M_AXI_ARREADY); always @(*) cover(M_AXI_RVALID); always @(*) cover(M_AXI_RVALID & M_AXI_RLAST); always @(*) cover(!r_stopped && cvr_full_frame); always @(*) cover(cvr_full_frame && phantom_start && !r_stopped); always @(*) if (cvr_hlast_rlast) begin // assume(M_AXI_RLAST == (rd_hlast && M_AXI_RVALID)); assume(M_AXI_ARREADY && M_AXI_RREADY); assume(M_AXIS_TREADY); assume(cfg_frame_addr[12:0] == 0); assume(cfg_line_step[3:0] == 0); end always @(*) cover(cvr_hlast_rlast && cvr_full_frame && phantom_start && !r_stopped); // }}} //////////////////////////////////////////////////////////////////////// // // Simplifying (careless) assumptions // {{{ //////////////////////////////////////////////////////////////////////// // // always @(posedge i_clk) if (!r_stopped || cfg_active) begin assume($stable(cfg_frame_addr)); assume($stable(cfg_frame_lines)); assume($stable(cfg_line_words)); assume($stable(cfg_line_step)); end always @(*) assume(!f_sequential); always @(*) assume(!f_biglines); always @(*) assume(!req_addr[C_AXI_ADDR_WIDTH]); always @(*) assume(!req_line_addr[C_AXI_ADDR_WIDTH]); // }}} // }}} `endif endmodule