dma_control.v 15.2 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
/*******************************************************************************
 * 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/> .
20 21 22 23 24 25 26 27 28 29 30 31 32
 *
 * Additional permission under GNU GPL version 3 section 7:
 * If you modify this Program, or any covered work, by linking or combining it
 * with independent modules provided by the FPGA vendor only (this permission
 * does not extend to any 3-rd party modules, "soft cores" or macros) under
 * different license terms solely for the purpose of generating binary "bitstream"
 * files and/or simulating the code, the copyright holders of this Program give
 * you the right to distribute the covered work without those independent modules
 * as long as the source code for them is available from the FPGA vendor free of
 * charge, and there is no dependence on any encrypted modules for simulating of
 * the combined code. This permission applies to you if the distributed code
 * contains all the components and scripts required to completely simulate it
 * with at least one of the Free Software programs.
Alexey Grebenkin's avatar
Alexey Grebenkin committed
33 34
 *******************************************************************************/
 /*
35
  * Later on most of address evaluation logic could be divided into 2 parts, which
Alexey Grebenkin's avatar
Alexey Grebenkin committed
36
  * could be presented as 2 instances of 1 parameterized module
37 38
  * + split data and address parts. Didnt do that because not sure if
  * virtual channels would be implemented in the future
Alexey Grebenkin's avatar
Alexey Grebenkin committed
39
  */
40 41 42 43
module dma_control(
   input   wire            sclk,   // sata clock
   input   wire            hclk,   // axi-hp clock
   input   wire            rst,
Alexey Grebenkin's avatar
Alexey Grebenkin committed
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
   // 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 command iface
   input   wire            adp_busy,
   output  wire    [31:7]  adp_addr,
   output  wire            adp_type,
   output  wire            adp_val,

   // sata host command iface
   input   wire            host_ready_for_cmd,
   output  wire            host_new_cmd,
   output  wire    [1:0]   host_cmd_type,
   output  wire    [31:0]  host_sector_count,
   output  wire    [31:0]  host_sector_addr,

   // 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,
   output  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
);
//////////////////////////////////////////////////////////////////////////////////////
//// ADDRESS
//////////////////////////////////////////////////////////////////////////////////////
wire    dma_done_adp;
wire    dma_done_host;
assign  dma_done = dma_done_host & dma_done_adp;

reg     adp_busy_sclk;
/*
 * Commands to sata host fsm
 */
// for now only 2 states: idle and send a pulse
reg     host_issued;
wire    host_issued_set;
wire    host_issued_clr;

assign  dma_done_host   = host_issued;

assign  host_issued_set = ~adp_busy_sclk & host_ready_for_cmd & dma_start;
assign  host_issued_clr = dma_done;

always @ (posedge sclk)
    host_issued <= (host_issued | host_issued_set) & ~host_issued_clr & ~rst;

// drive iface signals
assign  host_new_cmd        = host_issued_set;
112
assign  host_cmd_type       = {1'b0, dma_type};
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
assign  host_sector_count   = sector_cnt;
assign  host_sector_addr    = lba;

/*
 * Commands to adapter fsm
 */
reg     [33:0]  quarter_sector_cnt;
wire            last_data; // last 128 bytes of data are transmitted now
wire            adp_val_sclk;
reg     [31:7]  current_addr;
reg             current_type;


// fsm itself
wire    state_idle;
reg     state_wait_busy;
reg     state_wait_done;

wire    set_wait_busy;
wire    set_wait_done;
wire    clr_wait_busy;
wire    clr_wait_done;

assign  set_wait_busy = state_idle      & host_issued_set // same start pulse for both fsms
                      | state_wait_done & clr_wait_done & ~last_data; // still have some data to transmit within a current dma request
assign  set_wait_done = state_wait_busy & clr_wait_busy;

assign  clr_wait_busy =  adp_busy_sclk;
assign  clr_wait_done = ~adp_busy_sclk;

assign  state_idle = ~state_wait_busy & ~state_wait_done;
always @ (posedge sclk)
begin
    state_wait_busy <= (state_wait_busy | set_wait_busy) & ~clr_wait_busy & ~rst;
    state_wait_done <= (state_wait_done | set_wait_done) & ~clr_wait_done & ~rst;
end

150
assign  adp_val_sclk = set_wait_busy;
151 152 153 154 155 156 157 158 159
// conrol signals resync
reg             adp_val_r;
reg             adp_val_rr;
always @ (posedge hclk)
begin
    adp_val_r   <= adp_val_sclk;
    adp_val_rr  <= adp_val_r;
end

160
assign  adp_addr = current_addr[31:7];
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 191 192 193 194 195 196 197 198
assign  adp_type = current_type;
assign  adp_val  = adp_val_rr;

// Maintaining correct adp_busy level @ sclk
// assuming busy won't toggle rapidly, can afford not implementing handshakes
wire    adp_busy_sclk_set;
wire    adp_busy_sclk_clr;
wire    adp_busy_set;
wire    adp_busy_clr;
reg     adp_busy_r;

assign  adp_busy_set = adp_busy & ~adp_busy_r;
assign  adp_busy_clr = ~adp_busy & adp_busy_r;

always @ (posedge sclk)
    adp_busy_sclk   <= (adp_busy_sclk | adp_busy_sclk_set) & ~rst & ~adp_busy_sclk_clr;

always @ (posedge hclk)
    adp_busy_r <= adp_busy;

pulse_cross_clock adp_busy_set_pulse(
    .rst        (rst),
    .src_clk    (hclk),
    .dst_clk    (sclk),
    .in_pulse   (adp_busy_set),
    .out_pulse  (adp_busy_sclk_set),
    .busy       ()
);

pulse_cross_clock adp_busy_clr_pulse(
    .rst        (rst),
    .src_clk    (hclk),
    .dst_clk    (sclk),
    .in_pulse   (adp_busy_clr),
    .out_pulse  (adp_busy_sclk_clr),
    .busy       ()
);

199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
// synchronize with host fsm
reg     adp_done;
wire    adp_done_clr;
wire    adp_done_set;

assign  dma_done_adp = adp_done;

assign  adp_done_set = state_wait_done & clr_wait_done & ~set_wait_busy; // = state_wait_done & set_idle;
assign  adp_done_clr = dma_done;
always @ (posedge sclk)
    adp_done <= (adp_done | adp_done_set) & ~adp_done_clr & ~rst;


// calculate sent sector count
// 1 sector = 512 bytes for now => 1 quarter_sector = 128 bytes
always @ (posedge sclk)
    quarter_sector_cnt <= ~set_wait_busy ? quarter_sector_cnt :
                              state_idle ? 34'h0 :                    // new dma request
                                           quarter_sector_cnt + 1'b1; // same dma request, next 128 bytes

// flags if we're currently sending the last data piece of dma transaction
assign  last_data = (sector_cnt == quarter_sector_cnt[33:2] + 1'b1) & (&quarter_sector_cnt[1:0]);

// calculate outgoing address
// increment every transaction to adapter
always @ (posedge sclk)
225 226 227
    current_addr <= ~set_wait_busy ? current_addr[31:7] :
                        state_idle ? mem_address[31:7] :           // new dma request
                                     current_addr[31:7] + 1'b1; // same dma request, next 128 bytes
228 229 230 231 232

always @ (posedge sclk)
    current_type <= ~set_wait_busy ? current_type :
                        state_idle ? dma_type :           // new dma request
                                     current_type;        // same dma request, next 128 bytes
233 234 235 236

//////////////////////////////////////////////////////////////////////////////////////
//// DATA
//////////////////////////////////////////////////////////////////////////////////////
Alexey Grebenkin's avatar
Alexey Grebenkin committed
237 238 239 240 241 242 243 244 245
/*
 * 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
246 247
reg     [9:0]   from_rd_addr_gr;
reg     [8:0]   from_wr_addr_gr;
Alexey Grebenkin's avatar
Alexey Grebenkin committed
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
// 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
279
    from_wr_addr_gr_r   <= rst ?  9'h0 : from_wr_addr_gr;
280
    from_wr_addr_gr_rr  <= rst ?  9'h0 : from_wr_addr_gr_r;
Alexey Grebenkin's avatar
Alexey Grebenkin committed
281 282 283 284
end
// read address -> hclk (wr) domain to compare 
always @ (posedge hclk)
begin
285
    from_rd_addr_gr_r   <= rst ? 10'h0 : from_rd_addr_gr;
286
    from_rd_addr_gr_rr  <= rst ? 10'h0 : from_rd_addr_gr_r;
Alexey Grebenkin's avatar
Alexey Grebenkin committed
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
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
350 351
reg     [8:0]   to_rd_addr_gr;
reg     [9:0]   to_wr_addr_gr;
Alexey Grebenkin's avatar
Alexey Grebenkin committed
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
// 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;
369 370
// sclk domain counters
always @ (posedge sclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
371 372 373 374
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
375 376
// hclk domain counters
always @ (posedge hclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
377 378 379 380
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
381 382
// write address -> hclk (rd) domain to compare 
always @ (posedge hclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
383
begin
384
    to_wr_addr_gr_r   <= rst ? 10'h0 : to_wr_addr_gr;
385
    to_wr_addr_gr_rr  <= rst ? 10'h0 : to_wr_addr_gr_r;
Alexey Grebenkin's avatar
Alexey Grebenkin committed
386
end
387 388
// read address -> sclk (wr) domain to compare 
always @ (posedge sclk)
Alexey Grebenkin's avatar
Alexey Grebenkin committed
389
begin
390
    to_rd_addr_gr_r   <= rst ?  9'h0 : to_rd_addr_gr;
391
    to_rd_addr_gr_rr  <= rst ?  9'h0 : to_rd_addr_gr_r;
Alexey Grebenkin's avatar
Alexey Grebenkin committed
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
end
// translate resynced write address into ordinary (non-gray) address
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:
408 409
// 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
410 411 412
//  => we can say if the fifo have the possibility to be full
//     since actual to_rd_addr could only be incremented
//
413 414
// 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
415 416
//  => we can say if the fifo have the possibility to be empty
//     since actual to_wr_addr could only be incremented
417 418
assign  to_full   = to_wr_addr  == {to_rd_addr_r, 1'b0} + 1'b1;
assign  to_empty  = to_wr_addr_r == {to_rd_addr, 1'b0}; // overflows must never be achieved
Alexey Grebenkin's avatar
Alexey Grebenkin committed
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
// 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