dma_control.v 9.01 KB
Newer Older
Alexey Grebenkin's avatar
Alexey Grebenkin committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 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 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
/*******************************************************************************
 * Module: dma_control
 * Date: 2015-07-11  
 * Author: Alexey     
 * Description: temporary dma request control logic
 *
 * Copyright (c) 2015 Elphel, Inc.
 * dma_control.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.
 *
 * dma_control.v file 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/> .
 *******************************************************************************/
 /*
  * Later on most of address evaluation logic could divided into 2 parts, which
  * could be presented as 2 instances of 1 parameterized module
  */
 module dma_control(
    input   wire            sclk,   // sata clock
    input   wire            hclk,   // axi-hp clock
    input   wire            rst,

    // registers iface
    input   wire    [31:7]  mem_address,
    input   wire    [31:0]  lba,
    input   wire    [31:0]  sector_cnt,
    input   wire            dma_type,
    input   wire            dma_start,
    output  wire            dma_done,

    // adapter data iface
    // to main memory
    output  wire    [63:0]  to_data,
    output  wire            to_val,
    input   wire            to_ack,
    // from main memory
    input   wire    [63:0]  from_data,
    input   wire            from_val,
    input   wire            from_ack

    // sata host iface
    // data from sata host
    input   wire    [31:0]  in_data,
    output  wire            in_val,
    input   wire            in_busy,
    // data to sata host
    output  wire    [31:0]  out_data,
    output  wire            out_val,
    input   wire            out_busy
 );

/*
 * from main memory resyncronisation circuit
 */
reg     [9:0]   from_rd_addr;
reg     [8:0]   from_wr_addr;
// incremened addresses
wire    [8:0]   from_wr_next_addr;
wire    [9:0]   from_rd_next_addr;
// gray coded addresses
reg     [9:0]   from_rd_addr;
reg     [8:0]   from_wr_addr;
// anti-metastability shift registers for gray-coded addresses
reg     [9:0]   from_rd_addr_gr_r;
reg     [8:0]   from_wr_addr_gr_r;
reg     [9:0]   from_rd_addr_gr_rr;
reg     [8:0]   from_wr_addr_gr_rr;
// resynced to opposite clks addresses 
wire    [9:0]   from_rd_addr_r;
wire    [8:0]   from_wr_addr_r;
// fifo states
wire            from_full;      // MAY BE full. ~full -> MUST NOT be full
wire            from_empty;     // MAY BE empty. ~empty -> MUST NOT be empty
wire            from_re;
wire            from_we;

assign  from_wr_next_addr = from_wr_addr + 1'b1;
assign  from_rd_next_addr = from_rd_addr + 1'b1;
// hclk domain counters
always @ (posedge hclk)
begin
    from_wr_addr        <= rst ?  9'h0 : from_we ? from_wr_next_addr : from_wr_addr;
    from_wr_addr_gr     <= rst ?  9'h0 : from_we ? from_wr_next_addr ^ {1'b0, from_wr_next_addr[8:1]} : from_wr_addr_gr;
end
// sclk domain counters
always @ (posedge sclk)
begin
    from_rd_addr        <= rst ? 10'h0 : from_re ? from_rd_next_addr : from_rd_addr;
    from_rd_addr_gr     <= rst ? 10'h0 : from_re ? from_rd_next_addr ^ {1'b0, from_rd_next_addr[9:1]} : from_rd_addr_gr;
end
// write address -> sclk (rd) domain to compare 
always @ (posedge sclk)
begin
    from_wr_addr_gr_r   <= rst ?  9'h0 : from_wr_addr;
    from_wr_addr_gr_rr  <= rst ?  9'h0 : from_wr_addr_rr;
end
// read address -> hclk (wr) domain to compare 
always @ (posedge hclk)
begin
    from_rd_addr_gr_r   <= rst ? 10'h0 : from_rd_addr;
    from_rd_addr_gr_rr  <= rst ? 10'h0 : from_rd_addr_rr;
end
// translate resynced write address into ordinary (non-gray) address
genvar ii;
generate
for (ii = 0; ii < 9; ii = ii + 1)
begin: from_wr_antigray
    assign  from_wr_addr_r[ii] = ^from_wr_addr_gr_rr[8:ii];
end
endgenerate
// translate resynced read address into ordinary (non-gray) address
generate
for (ii = 0; ii < 10; ii = ii + 1)
begin: from_rd_antigray
    assign  from_rd_addr_r[ii] = ^from_rd_addr_gr_rr[9:ii];
end
endgenerate
// so we've got the following:
// hclk domain: from_wr_addr   - current write address
//              from_rd_addr_r - read address some hclk ticks ago
//  => we can say if the fifo have the possibility to be full
//     since actual from_rd_addr could only be incremented
//
// sclk domain: from_rd_addr   - current read address
//              from_wr_addr_r - write address some sclk ticks ago
//  => we can say if the fifo have the possibility to be empty
//     since actual from_wr_addr could only be incremented
assign  from_full   = {from_wr_addr, 1'b0}  == from_rd_addr_r + 1'b1;
assign  from_empty  = {from_wr_addr_r, 1'b0} == from_rd_addr; // overflows must never be achieved
// calculate bus responses in order to fifo status:
assign  from_ack    = from_val & ~from_full;
assign  out_val     = ~out_busy & ~from_empty;

assign  from_re     = out_val;
assign  from_we     = from_ack;

// data buffer, recieves 64-bit data from main memory, directs it to sata host
ram_512x64w_1kx32r #(
    .REGISTERS  (0)
)
dma_data_from_mem (
    .rclk       (sclk),
    .raddr      (from_rd_addr),
    .ren        (from_re),
    .regen      (1'b0),
    .data_out   (out_data),
    .wclk       (hclk),
    .waddr      (from_wr_addr),
    .we         (from_we),
    .web        (8'hff),
    .data_in    (from_data)
);

/////////////////////////////////////////////////////////////////////////////////
/*
 * to main memory resyncronisation circuit
 */

reg     [8:0]   to_rd_addr;
reg     [9:0]   to_wr_addr;
// incremened addresses
wire    [9:0]   to_wr_next_addr;
wire    [8:0]   to_rd_next_addr;
// gray coded addresses
reg     [8:0]   to_rd_addr;
reg     [9:0]   to_wr_addr;
// anti-metastability shift registers for gray-coded addresses
reg     [8:0]   to_rd_addr_gr_r;
reg     [9:0]   to_wr_addr_gr_r;
reg     [8:0]   to_rd_addr_gr_rr;
reg     [9:0]   to_wr_addr_gr_rr;
// resynced to opposite clks addresses 
wire    [8:0]   to_rd_addr_r;
wire    [9:0]   to_wr_addr_r;
// fifo states
wire            to_full;      // MAY BE full. ~full -> MUST NOT be full
wire            to_empty;     // MAY BE empty. ~empty -> MUST NOT be empty
wire            to_re;
wire            to_we;


assign  to_wr_next_addr = to_wr_addr + 1'b1;
assign  to_rd_next_addr = to_rd_addr + 1'b1;
191 192
// sclk domain counters
always @ (posedge sclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
193 194 195 196
begin
    to_wr_addr        <= rst ? 10'h0 : to_we ? to_wr_next_addr : to_wr_addr;
    to_wr_addr_gr     <= rst ? 10'h0 : to_we ? to_wr_next_addr ^ {1'b0, to_wr_next_addr[9:1]} : to_wr_addr_gr;
end
197 198
// hclk domain counters
always @ (posedge hclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
199 200 201 202
begin
    to_rd_addr        <= rst ?  9'h0 : to_re ? to_rd_next_addr : to_rd_addr;
    to_rd_addr_gr     <= rst ?  9'h0 : to_re ? to_rd_next_addr ^ {1'b0, to_rd_next_addr[8:1]} : to_rd_addr_gr;
end
203 204
// write address -> hclk (rd) domain to compare 
always @ (posedge hclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
205 206 207 208
begin
    to_wr_addr_gr_r   <= rst ? 10'h0 : to_wr_addr;
    to_wr_addr_gr_rr  <= rst ? 10'h0 : to_wr_addr_rr;
end
209 210
// read address -> sclk (wr) domain to compare 
always @ (posedge sclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
begin
    to_rd_addr_gr_r   <= rst ?  9'h0 : to_rd_addr;
    to_rd_addr_gr_rr  <= rst ?  9'h0 : to_rd_addr_rr;
end
// translate resynced write address into ordinary (non-gray) address
genvar ii;
generate
for (ii = 0; ii < 10; ii = ii + 1)
begin: to_wr_antigray
    assign  to_wr_addr_r[ii] = ^to_wr_addr_gr_rr[9:ii];
end
endgenerate
// translate resynced read address into ordinary (non-gray) address
generate
for (ii = 0; ii < 9; ii = ii + 1)
begin: to_rd_antigray
    assign  to_rd_addr_r[ii] = ^to_rd_addr_gr_rr[8:ii];
end
endgenerate
// so we've got the following:
231 232
// sclk domain: to_wr_addr   - current write address
//              to_rd_addr_r - read address some sclk ticks ago
Alexey Grebenkin's avatar
Alexey Grebenkin committed
233 234 235
//  => we can say if the fifo have the possibility to be full
//     since actual to_rd_addr could only be incremented
//
236 237
// hclk domain: to_rd_addr   - current read address
//              to_wr_addr_r - write address some hclk ticks ago
Alexey Grebenkin's avatar
Alexey Grebenkin committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
//  => we can say if the fifo have the possibility to be empty
//     since actual to_wr_addr could only be incremented
assign  to_full   = {to_wr_addr, 1'b0}  == to_rd_addr_r + 1'b1;
assign  to_empty  = {to_wr_addr_r, 1'b0} == to_rd_addr; // overflows must never be achieved
// calculate bus responses in order to fifo status:
assign  to_val    = ~to_empty;
assign  in_val    = ~in_busy & ~to_full;

assign  to_re     = to_ack;
assign  to_we     = in_val;

// data buffer, recieves 32-bit data from sata host, directs it to the main memory
ram_1kx32w_512x64r #(
    .REGISTERS  (0)
)
dma_data_to_mem (
    .rclk       (hclk),
    .raddr      (to_rd_addr),
    .ren        (to_re),
    .regen      (1'b0),
    .data_out   (to_data),
    .wclk       (sclk),
    .waddr      (to_wr_addr),
    .we         (to_we),
    .web        (4'hf),
    .data_in    (in_data)
);


 endmodule