cmd_mux.v 10.9 KB
Newer Older
1 2 3
/*******************************************************************************
 * Module: cmd_mux
 * Date:2015-01-11  
4
 * Author: Andrey Filippov     
5 6
 * Description: Command multiplexer between AXI and frame-based command sequencer
 *
7
 * Copyright (c) 2015 Elphel, Inc.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * cmd_mux.v is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  cmd_mux.v is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/> .
 *******************************************************************************/
`timescale 1ns/1ps

module  cmd_mux #(
24
    parameter AXI_WR_ADDR_BITS=    14,
25 26 27 28 29 30 31 32 33 34 35 36
    parameter CONTROL_ADDR =       'h0000, // AXI write address of control write registers
    parameter CONTROL_ADDR_MASK =  'h3800, // AXI write address of control registers
    parameter NUM_CYCLES_LOW_BIT=   6, // decode addresses [NUM_CYCLES_LOW_BIT+:5] into command a/d length
    parameter NUM_CYCLES_00 =       2, // 2-cycle 000.003f
    parameter NUM_CYCLES_01 =       4, // 4-cycle 040.007f
    parameter NUM_CYCLES_02 =       3, // 3-cycle 080.00bf
    parameter NUM_CYCLES_03 =       3, // 3-cycle 0c0.00ff
    parameter NUM_CYCLES_04 =       6, // 6-cycle 100.013f
    parameter NUM_CYCLES_05 =       6, // 6-cycle 140.017f
    parameter NUM_CYCLES_06 =       4, // 4-cycle 180.01bf
    parameter NUM_CYCLES_07 =       4, // 4-cycle 1c0.01ff
    parameter NUM_CYCLES_08 =       6, // 6-cycle 200.023f
37 38 39 40
    parameter NUM_CYCLES_09 =       6, //
    parameter NUM_CYCLES_10 =       6, //
    parameter NUM_CYCLES_11 =       6, //
    parameter NUM_CYCLES_12 =       6, //
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
    parameter NUM_CYCLES_13 =       5, // 5-cycle - not yet used
    parameter NUM_CYCLES_14 =       6, // 6-cycle - not yet used
    parameter NUM_CYCLES_15 =       9, // single-cycle
    parameter NUM_CYCLES_16 =       6,  //
    parameter NUM_CYCLES_17 =       6,  //
    parameter NUM_CYCLES_18 =       6,  //
    parameter NUM_CYCLES_19 =       6,  //
    parameter NUM_CYCLES_20 =       6,  //
    parameter NUM_CYCLES_21 =       6,  //
    parameter NUM_CYCLES_22 =       6,  //
    parameter NUM_CYCLES_23 =       6,  //
    parameter NUM_CYCLES_24 =       6,  //
    parameter NUM_CYCLES_25 =       6,  //
    parameter NUM_CYCLES_26 =       6,  //
    parameter NUM_CYCLES_27 =       6,  //
    parameter NUM_CYCLES_28 =       6,  //
    parameter NUM_CYCLES_29 =       6,  //
    parameter NUM_CYCLES_30 =       6,  //
    parameter NUM_CYCLES_31 =       6  //
    
61
) (
62
    input                         axi_clk,
63
    input                         mclk,
Andrey Filippov's avatar
Andrey Filippov committed
64 65
    input                         mrst, // @posedge mclk - sync reset
    input                         arst, // @posedge axi_clk - sync reset
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
    // direct commands from AXI. No wait but for multi-cycle output and command sequencer (having higher priority)
    input  [AXI_WR_ADDR_BITS-1:0] pre_waddr,     // AXI write address, before actual writes (to generate busy), valid@start_burst
    input                         start_wburst, // burst start - should generate ~ready (should be AND-ed with !busy internally) 
    input  [AXI_WR_ADDR_BITS-1:0] waddr,        // write address, valid with wr_en
    input                         wr_en,        // write enable 
    input                  [31:0] wdata,        // write data, valid with waddr and wr_en
    output                        busy,         // interface busy (combinatorial delay from start_wburst and pre_addr), controls AXI FIFO
    
    // frame-based commands from the command sequencer (no wait but for multi-cycle output 
    input  [AXI_WR_ADDR_BITS-1:0] cseq_waddr,   // write address, valid with cseq_wr_en
    input                         cseq_wr_en,   // write enable 
    input                  [31:0] cseq_wdata,   // write data, valid with cseq_waddr and cseq_wr_en
    output                        cseq_ackn,    // command sequencer address/data accepted
    // Write address /data/strobe to slaves. Both parallel and byte-serial data available. COmbined from AXI and command sequencer
    output [AXI_WR_ADDR_BITS-1:0] par_waddr,    // parallel address
    output                 [31:0] par_data,     // parallel 32-bit data
    output                  [7:0] byte_ad,      // byte-wide address/data (AL-AH-DB0-DB1-DB2-DB3)
    output                        ad_stb        // low address output strobe (and parallel A/D)
);
// Minimal - 1 cycle, AH=DB0=DB1=DB2=DB3=0;
    reg busy_r=0;
    reg selected=0; // address range to be processed here (outside - buffer(s) and command sequencer?)
    wire fifo_half_empty; // just debugging with (* keep = "true" *)
    wire selected_w;
    wire ss;        // current command (in par_waddr) is a single-cycle one
    reg                    [47:0] par_ad;
    reg                           ad_stb_r;       // low address output strobe (and parallel A/D)
    reg                           cmdseq_full_r;  // address/data from the command sequencer is loaded to internal register (cseq_waddr_r,cseq_wdata_r)
    reg    [AXI_WR_ADDR_BITS-1:0] cseq_waddr_r;   // registered command address from the sequencer
    reg                    [31:0] cseq_wdata_r;   // registered command data from the sequencer
    reg                     [3:0] seq_length;     // encoded ROM output - number of cycles in command sequence, [3] - single cycle 
    reg                     [4:0] seq_busy_r;     // shift register loaded by decoded seq_length
98
    wire                    [4:0] seq_length_rom_a; // address range used to determine command length
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

    wire  can_start_w;  // can start command cycle (either from sequencer or from AXI)
    wire  start_w;      // start cycle
    wire  start_axi_w;  // start cycle from the AXI (==fifo_re)
    wire fifo_nempty;
    wire [AXI_WR_ADDR_BITS-1:0] waddr_fifo_out;
    wire                 [31:0] wdata_fifo_out;
    
    assign selected_w=((pre_waddr ^ CONTROL_ADDR) & CONTROL_ADDR_MASK)==0;

    assign busy=busy_r && (start_wburst? selected_w: selected);// should be just combinatorial delay from start_wburst and decoded command
    assign par_waddr=par_ad[AXI_WR_ADDR_BITS-1:0];    // parallel address
    assign par_data=par_ad[47:16];     // parallel 32-bit data
    assign byte_ad=par_ad[7:0];      // byte-wide address/data (AL-AH-DB0-DB1-DB2-DB3)
    assign ad_stb=ad_stb_r;       // low address output strobe (and parallel A/D)
    
115
    assign seq_length_rom_a=par_ad[NUM_CYCLES_LOW_BIT+:5];
116 117
    assign ss= seq_length[3];

Andrey Filippov's avatar
Andrey Filippov committed
118 119
    always @ (posedge axi_clk) begin
        if (arst)              selected <= 1'b0;
120
        else if (start_wburst) selected <= selected_w;
Andrey Filippov's avatar
Andrey Filippov committed
121
        if (arst)              busy_r <= 1'b0;
122 123 124 125 126 127 128
        else                   busy_r <= !fifo_half_empty;
    end
    
// ROM command length decoder TODO: put actual data
//    always @ (seq_length_rom_a) begin
    always @*
        case (seq_length_rom_a)  // just temporary - fill out later
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
            5'h00:seq_length <= NUM_CYCLES_00;
            5'h01:seq_length <= NUM_CYCLES_01;
            5'h02:seq_length <= NUM_CYCLES_02;
            5'h03:seq_length <= NUM_CYCLES_03;
            5'h04:seq_length <= NUM_CYCLES_04;
            5'h05:seq_length <= NUM_CYCLES_05;
            5'h06:seq_length <= NUM_CYCLES_06;
            5'h07:seq_length <= NUM_CYCLES_07;
            5'h08:seq_length <= NUM_CYCLES_08;
            5'h09:seq_length <= NUM_CYCLES_09;
            5'h0a:seq_length <= NUM_CYCLES_10;
            5'h0b:seq_length <= NUM_CYCLES_11;
            5'h0c:seq_length <= NUM_CYCLES_12;
            5'h0d:seq_length <= NUM_CYCLES_13;
            5'h0e:seq_length <= NUM_CYCLES_14;
            5'h0f:seq_length <= NUM_CYCLES_15;
            5'h10:seq_length <= NUM_CYCLES_16;
            5'h11:seq_length <= NUM_CYCLES_17;
            5'h12:seq_length <= NUM_CYCLES_18;
            5'h13:seq_length <= NUM_CYCLES_19;
            5'h14:seq_length <= NUM_CYCLES_20;
            5'h15:seq_length <= NUM_CYCLES_21;
            5'h16:seq_length <= NUM_CYCLES_22;
            5'h17:seq_length <= NUM_CYCLES_23;
            5'h18:seq_length <= NUM_CYCLES_24;
            5'h19:seq_length <= NUM_CYCLES_25;
            5'h1a:seq_length <= NUM_CYCLES_26;
            5'h1b:seq_length <= NUM_CYCLES_27;
            5'h1c:seq_length <= NUM_CYCLES_28;
            5'h1d:seq_length <= NUM_CYCLES_29;
            5'h1e:seq_length <= NUM_CYCLES_30;
            5'h1f:seq_length <= NUM_CYCLES_31;
161
        endcase
Andrey Filippov's avatar
Andrey Filippov committed
162 163
    always @ (posedge mclk) begin
        if (mrst) seq_busy_r<=0;
164 165 166 167 168 169 170 171 172 173
        else begin
            if (ad_stb) begin
                case (seq_length)
                    4'h2:    seq_busy_r<=5'h01;
                    4'h3:    seq_busy_r<=5'h03;
                    4'h4:    seq_busy_r<=5'h07;
                    4'h5:    seq_busy_r<=5'h0f;
                    4'h6:    seq_busy_r<=5'h1f;
                    default: seq_busy_r<=5'h00;
                endcase
Andrey Filippov's avatar
Andrey Filippov committed
174
            end else seq_busy_r <= {1'b0,seq_busy_r[4:1]};
175 176 177 178 179 180
        end
    end
    
    assign can_start_w=  ad_stb_r? ss: !seq_busy_r[1];
    assign start_axi_w=  can_start_w && ~cmdseq_full_r && fifo_nempty;
    assign start_w=      can_start_w && (cmdseq_full_r || fifo_nempty);
Andrey Filippov's avatar
Andrey Filippov committed
181 182 183
    always @ (posedge mclk) begin
        if (mrst) ad_stb_r <= 0;
        else      ad_stb_r <= start_w;
184 185 186
    end
    always @ (posedge mclk) begin
        if (start_w) par_ad <={cmdseq_full_r?cseq_wdata_r:wdata_fifo_out,{(16-AXI_WR_ADDR_BITS){1'b0}},cmdseq_full_r?cseq_waddr_r:waddr_fifo_out};
Andrey Filippov's avatar
Andrey Filippov committed
187
        else par_ad <={8'b0,par_ad[47:8]};
188 189 190 191
    end
    
    assign  cseq_ackn= cseq_wr_en && (!cmdseq_full_r || can_start_w); // cmddseq_full has priority over axi, so (can_start_w && cmdseq_full_r)
    
Andrey Filippov's avatar
Andrey Filippov committed
192 193
    always @ (posedge mclk) begin
        if (mrst) cmdseq_full_r <= 0;
194 195 196 197 198 199 200 201 202 203 204 205 206 207
        else cmdseq_full_r <= cseq_ackn || (cmdseq_full_r && !can_start_w);
    end
    always @ (posedge mclk) begin
        if (cseq_ackn) begin
            cseq_waddr_r <= cseq_waddr;
            cseq_wdata_r <= cseq_wdata;
        end
    end        

    /* FIFO to cross clock boundary */
    fifo_cross_clocks #(
        .DATA_WIDTH  (AXI_WR_ADDR_BITS+32),
        .DATA_DEPTH  (4)
    ) fifo_cross_clocks_i (
Andrey Filippov's avatar
Andrey Filippov committed
208 209 210
        .rst         (1'b0), // input
        .rrst        (mrst), // input
        .wrst        (arst), // input
211
        .rclk        (mclk), // input
Andrey Filippov's avatar
Andrey Filippov committed
212
        .wclk        (axi_clk), // input
213 214 215 216 217 218 219 220 221 222
        .we          (wr_en && selected), // input
        .re          (start_axi_w), // input
        .data_in     ({waddr[AXI_WR_ADDR_BITS-1:0],wdata[31:0]}), // input[15:0] 
        .data_out    ({waddr_fifo_out[AXI_WR_ADDR_BITS-1:0],wdata_fifo_out[31:0]}), // output[15:0] 
        .nempty      (fifo_nempty), // output
        .half_empty  (fifo_half_empty) // output
    );


endmodule