//////////////////////////////////////////////////////////////////////////////// // // Filename: axivcamera // {{{ // Project: WB2AXIPSP: bus bridges and other odds and ends // // Purpose: Reads a video frame from a source and writes the result to // memory. // // 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. // bits 15-8: FRAMES // Indicates the number of frames you want to copy. Set this to // 0 to continuously copy, or a finite number to grab only that // many frames. // // As a feature, this isn't perhaps the most useful, since every // frame will be written to the same location. A more useful // approach would be to increase the desired number of lines to // (NLINES * NFRAMES), and then to either leave this number at zero // or set it to one. // // 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 (rounded down to the neareset // word) into this value. This is a recommended approach to // setting 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. To convert from bytes to words, // round any fractional part upwards (i.e. ceil()). The core // will naturally round down internally. // // 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) // // KNOWN ISSUES: // // Does not support interlacing (yet). (Interlacing is not on my "todo" // list, so it might take a while to get said support if you need it.) // // Does not support unaligned addressing. The frame buffer address, and // the line step, must all be word aligned. While nothing is stopping // me from supporting unaligned addressing, it was such a pain to do the // last time that I might just hold off until I have a paying customer // who wants it. // // Line bytes that include a fraction of a word are rounded down and not // up. // }}} // // 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 axivcamera #( // {{{ 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_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 OPT_LGFIFO = OPT_LGMAXBURST+1, localparam LGFIFO = (OPT_LGFIFO < OPT_LGMAXBURST+1) ? OPT_LGMAXBURST+1 : OPT_LGFIFO, // // AXI_ID is the ID we will use for all of our AXI transactions parameter AXI_ID = 0, // // OPT_IGNORE_HLAST parameter [0:0] OPT_IGNORE_HLAST = 0, // // OPT_TUSER_IS_SOF. Xilinx and I chose different encodings. // I encode TLAST == VLAST, and TUSER == HLAST (optional). // Xilinx likes 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 incoming video stream data/pixel interface // {{{ input wire S_AXIS_TVALID, output wire S_AXIS_TREADY, input wire [C_AXI_DATA_WIDTH-1:0] S_AXIS_TDATA, input wire /* VLAST */ S_AXIS_TLAST, input wire /* HLAST */ S_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) write-only interface // {{{ output wire M_AXI_AWVALID, input wire M_AXI_AWREADY, output wire [C_AXI_ID_WIDTH-1:0] M_AXI_AWID, output wire [C_AXI_ADDR_WIDTH-1:0] M_AXI_AWADDR, output wire [7:0] M_AXI_AWLEN, output wire [2:0] M_AXI_AWSIZE, output wire [1:0] M_AXI_AWBURST, output wire M_AXI_AWLOCK, output wire [3:0] M_AXI_AWCACHE, output wire [2:0] M_AXI_AWPROT, output wire [3:0] M_AXI_AWQOS, // output wire M_AXI_WVALID, input wire M_AXI_WREADY, output wire [C_AXI_DATA_WIDTH-1:0] M_AXI_WDATA, output wire [C_AXI_DATA_WIDTH/8-1:0] M_AXI_WSTRB, output wire M_AXI_WLAST, // input wire M_AXI_BVALID, output wire M_AXI_BREADY, input wire [C_AXI_ID_WIDTH-1:0] M_AXI_BID, input wire [1:0] M_AXI_BRESP // }}} // }}} ); // 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, CBIT_LOST_SYNC = 4; 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, lost_sync; reg cfg_active, cfg_zero_length, cfg_continuous; 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, fifo_data; wire [LGFIFO:0] fifo_fill; wire fifo_full, fifo_empty, fifo_vlast, fifo_hlast; 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 axi_awvalid, axi_wvalid, axi_wlast, phantom_start, start_burst, aw_none_outstanding; reg [C_AXI_ADDR_WIDTH-1:0] axi_awaddr; reg [7:0] axi_awlen, next_awlen; reg [8:0] wr_pending; reg [15:0] aw_bursts_outstanding; // // reg vlast; // reg [15:0] r_frame_lines, r_line_step; reg [16-ADDRLSB-1:0] next_line_words; reg req_hlast, req_vlast, req_newline; reg [15:0] req_nlines; reg [7:0] req_nframes; reg [16-ADDRLSB-1:0] req_line_words; reg [C_AXI_ADDR_WIDTH:0] req_addr, req_line_addr, next_line_addr; // reg wr_hlast, wr_vlast; reg [15:0] wr_lines; reg [16-ADDRLSB-1:0] wr_line_beats; // wire no_fifo__available; // reg [LGMAXBURST-1:0] till_boundary; reg [LGFIFO:0] fifo_data_available; reg fifo_bursts_available; // }}} //////////////////////////////////////////////////////////////////////// // // 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 (r_stopped) begin if (axil_write_ready && awskd_addr == FBUF_CONTROL && wskd_strb[0] && wskd_data[CBIT_ERR]) begin r_err <= cfg_zero_length; soft_reset <= 0; end if (!r_err) 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_BVALID && M_AXI_BREADY && M_AXI_BRESP[1]) ||(req_addr[C_AXI_ADDR_WIDTH])) 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 : 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_continuous = 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); initial cfg_line_step = { DEF_WORDS_PER_LINE, {(ADDRLSB){1'b0}} }; 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); req_nframes <= 0; cfg_continuous <= 0; end else begin if (phantom_start && req_vlast && req_hlast && req_nframes > 0) req_nframes <= req_nframes - 1; if (axil_write_ready) case(awskd_addr) FBUF_CONTROL: begin if (wskd_strb[0]) cfg_active <= (cfg_active || r_stopped) && wskd_data[CBIT_ACTIVE] && (!r_err || wskd_data[CBIT_ERR]) && (!cfg_zero_length); if (!cfg_active && r_stopped && wskd_strb[1]) begin req_nframes <= wskd_data[15:8]; cfg_continuous <= (wskd_data[15:8] == 0); end if (!cfg_active && r_stopped) begin if (new_control[31:16] == 0) begin cfg_line_step <= 0; cfg_line_step[16-1:ADDRLSB] <= cfg_line_words; end else cfg_line_step <= new_control[31:16]; end end FBUF_FRAMEINFO: if (!cfg_active && r_stopped) begin { cfg_frame_lines, cfg_line_words } <= new_config[C_AXIL_DATA_WIDTH-1:ADDRLSB]; cfg_zero_length <= (new_config[31:16] == 0) ||(new_config[15:ADDRLSB] == 0); end FBUF_ADDRLO, FBUF_ADDRHI: if (!cfg_active && r_stopped) cfg_frame_addr <= new_wideaddr[C_AXI_ADDR_WIDTH-1:0]; default: begin end endcase if (M_AXI_BREADY && M_AXI_BVALID && M_AXI_BRESP[1]) cfg_active <= 0; if (req_addr[C_AXI_ADDR_WIDTH]) cfg_active <= 0; if (phantom_start && req_vlast && req_hlast && req_nframes <= 1 && !cfg_continuous) cfg_active <= 0; cfg_line_step[ADDRLSB-1:0] <= 0; cfg_frame_addr[ADDRLSB-1:0] <= 0; end // }}} // AXI-Lite read register data // {{{ always @(*) begin w_status_word = 0; w_status_word[31:16] = cfg_line_step; w_status_word[15:8] = req_nframes; w_status_word[CBIT_LOST_SYNC] = lost_sync; // 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= cfg_frame_lines); assign S_AXIS_VLAST = axis_last_line && S_AXIS_HLAST; end else begin : VLAST_LOCK assign S_AXIS_VLAST = S_AXIS_TLAST; assign S_AXIS_HLAST = S_AXIS_TUSER; /* initial S_AXIS_SOF = 0; always @(posedge S_AXI_ACLK) if (!S_AXI_ARESETN) S_AXIS_SOF <= 1'b0; else if (S_AXIS_TVALID && S_AXIS_TREADY) S_AXIS_SOF <= S_AXIS_VLAST; */ assign S_AXIS_SOF = 1'b0; // Verilator lint_off UNUSED wire unused_sof; assign unused_sof = &{ 1'b0, S_AXIS_SOF }; // Verilator lint_on UNUSED end endgenerate // Read/write signaling, lost_sync detection // {{{ assign reset_fifo = (!cfg_active || r_stopped || lost_sync); assign write_to_fifo = S_AXIS_TVALID && !lost_sync && !fifo_full; assign write_data = S_AXIS_TDATA; assign read_from_fifo = (M_AXI_WVALID && M_AXI_WREADY); assign S_AXIS_TREADY = 1'b1; initial lost_sync = 1; always @(posedge S_AXI_ACLK) begin if (!S_AXI_ARESETN || !cfg_active || r_stopped) lost_sync <= 0; else if (M_AXI_WVALID && M_AXI_WREADY &&((!OPT_IGNORE_HLAST&&(wr_hlast != fifo_hlast)) || (!wr_hlast && fifo_vlast) || (wr_vlast && wr_hlast && !fifo_vlast))) // Here is where we might possibly notice we've lost sync lost_sync <= 1; // lost_sync <= 1'b0; end // }}} generate if (LGFIFO > 0) begin : GEN_SPACE_AVAILBLE // Here's where we'll put the actual outgoing FIFO // {{{ reg [LGFIFO:0] next_data_available; always @(*) begin next_data_available = fifo_data_available; // Verilator lint_off WIDTH if (phantom_start) next_data_available = fifo_data_available - (M_AXI_AWLEN + 1); // Verilator lint_on WIDTH if (write_to_fifo) next_data_available = next_data_available + 1; end initial fifo_data_available = 0; initial fifo_bursts_available = 0; always @(posedge i_clk) if (reset_fifo) begin fifo_data_available <= 0; fifo_bursts_available <= 0; end else if (phantom_start || write_to_fifo) begin fifo_data_available <= next_data_available; fifo_bursts_available <= |next_data_available[LGFIFO:LGMAXBURST]; end sfifo #(.BW(C_AXI_DATA_WIDTH+2), .LGFLEN(LGFIFO)) sfifo(i_clk, reset_fifo, write_to_fifo, { S_AXIS_VLAST, (!OPT_IGNORE_HLAST && S_AXIS_HLAST),write_data}, fifo_full, fifo_fill, read_from_fifo, { fifo_vlast, fifo_hlast, fifo_data }, fifo_empty); // }}} end else begin : NO_FIFO // {{{ // // This isn't verified or tested. I'm not sure I'd expect this // to work at all. assign fifo_full = M_AXI_WVALID && !M_AXI_WREADY; assign fifo_fill = 0; assign fifo_empty = !S_AXIS_TVALID; assign fifo_vlast = S_AXIS_VLAST; assign fifo_hlast = (!OPT_IGNORE_HLAST && S_AXIS_HLAST); assign fifo_data = write_data; // }}} end endgenerate // }}} //////////////////////////////////////////////////////////////////////// // // Outoing frame address counting // {{{ //////////////////////////////////////////////////////////////////////// // // reg w_frame_needs_alignment, frame_needs_alignment, line_needs_alignment, line_multiple_bursts, req_needs_alignment, req_multiple_bursts; // frame_needs_alignment // {{{ always @(*) begin w_frame_needs_alignment = 0; if (|cfg_line_words[15-ADDRLSB:LGMAXBURST]) w_frame_needs_alignment = 1; if (~cfg_frame_addr[ADDRLSB +: LGMAXBURST] < cfg_line_words[LGMAXBURST-1:0]) w_frame_needs_alignment = 1; if (cfg_frame_addr[ADDRLSB +: LGMAXBURST] == 0) w_frame_needs_alignment = 0; end always @(posedge i_clk) if (!cfg_active && r_stopped) frame_needs_alignment <= w_frame_needs_alignment; // }}} // line_needs_alignment // {{{ always @(posedge i_clk) if (r_stopped) line_multiple_bursts <= (cfg_line_words >= (1<= (1< 0 && !fifo_bursts_available) begin if (!req_multiple_bursts && fifo_data_available[7:0] < req_line_words[7:0]) start_burst = 0; if (req_multiple_bursts && !fifo_bursts_available) start_burst = 0; end if (phantom_start || req_newline || lost_sync) start_burst = 0; // Can't start a new burst if the outgoing channel is still // stalled. if (M_AXI_AWVALID && !M_AXI_AWREADY) start_burst = 0; if (M_AXI_WVALID && (!M_AXI_WREADY || !M_AXI_WLAST)) start_burst = 0; // Don't let our aw_bursts_outstanding counter overflow if (aw_bursts_outstanding[15]) start_burst = 0; // Don't wrap around memory if (req_addr[C_AXI_ADDR_WIDTH]) start_burst = 0; // If the user wants us to stop, then stop if (soft_reset || !cfg_active || r_stopped) start_burst = 0; end // }}} // AWLEN // {{{ always @(*) till_boundary = ~req_addr[ADDRLSB +: LGMAXBURST]; always @(*) begin if (req_needs_alignment) next_awlen = till_boundary; else if (req_multiple_bursts) next_awlen = (1<