222 lines
5.5 KiB
Verilog
222 lines
5.5 KiB
Verilog
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Filename: zipdma_fsm.v
|
|
// {{{
|
|
// Project: 10Gb Ethernet switch
|
|
//
|
|
// Purpose: ZipDMA's control FSM
|
|
//
|
|
// Since the Wishbone bus can only accommodate either a read or a write
|
|
// transaction, large DMA transfers need to be broken up between reads
|
|
// and writes. This function accomplishes that purpose--issuing read
|
|
// requests of the zipdma_mm2s controller, followed by write requests
|
|
// of the zipdma_s2mm controller.
|
|
//
|
|
// Creator: Dan Gisselquist, Ph.D.
|
|
// Gisselquist Technology, LLC
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// }}}
|
|
// Copyright (C) 2023, Gisselquist Technology, LLC
|
|
// {{{
|
|
// This file is part of the ETH10G project.
|
|
//
|
|
// The ETH10G 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 zipdma_fsm #(
|
|
// {{{
|
|
parameter ADDRESS_WIDTH = 32, // Byte ADDR width
|
|
parameter LGDMALENGTH = ADDRESS_WIDTH,
|
|
parameter LGSUBLENGTH = 10
|
|
// }}}
|
|
) (
|
|
// {{{
|
|
input wire i_clk, i_reset,
|
|
input wire i_soft_reset,
|
|
// DMA control
|
|
// {{{
|
|
input wire i_dma_request,
|
|
output reg o_dma_busy,
|
|
output reg o_dma_err,
|
|
input wire [ADDRESS_WIDTH-1:0] i_src_addr,
|
|
input wire [ADDRESS_WIDTH-1:0] i_dst_addr,
|
|
input wire [LGDMALENGTH-1:0] i_length,
|
|
input wire [LGSUBLENGTH:0] i_transferlen,
|
|
|
|
output wire [LGDMALENGTH-1:0] o_remaining_len,
|
|
// }}}
|
|
input wire i_trigger,
|
|
// MM2S control
|
|
// {{{
|
|
output reg o_mm2s_request,
|
|
input wire i_mm2s_busy,
|
|
input wire i_mm2s_err,
|
|
input wire i_mm2s_inc,
|
|
// input wire [1:0] i_mm2s_size,
|
|
output reg [ADDRESS_WIDTH-1:0] o_mm2s_addr,
|
|
output wire [LGSUBLENGTH:0] o_mm2s_transferlen,
|
|
// }}}
|
|
// S2MM control
|
|
// {{{
|
|
output reg o_s2mm_request,
|
|
input wire i_s2mm_busy,
|
|
input wire i_s2mm_err,
|
|
input wire i_s2mm_inc,
|
|
// input wire [1:0] i_s2mm_size,
|
|
output reg [ADDRESS_WIDTH-1:0] o_s2mm_addr,
|
|
output wire [LGSUBLENGTH:0] o_s2mm_transferlen
|
|
// }}}
|
|
// }}}
|
|
);
|
|
|
|
// Local declarations
|
|
// {{{
|
|
localparam [1:0] S_IDLE = 2'b00,
|
|
S_WAIT = 2'b01,
|
|
S_READ = 2'b10,
|
|
S_WRITE = 2'b11;
|
|
|
|
reg [LGDMALENGTH-1:0] r_length;
|
|
reg [LGSUBLENGTH:0] r_transferlen;
|
|
reg [1:0] fsm_state;
|
|
// }}}
|
|
|
|
always @(posedge i_clk)
|
|
if (i_reset || i_soft_reset || i_mm2s_err || i_s2mm_err)
|
|
begin
|
|
// {{{
|
|
o_dma_busy <= 0;
|
|
r_length <= 0;
|
|
r_transferlen <= 0;
|
|
o_mm2s_request <= 0;
|
|
o_s2mm_request <= 0;
|
|
|
|
o_mm2s_addr <= 0;
|
|
o_s2mm_addr <= 0;
|
|
|
|
fsm_state <= S_IDLE;
|
|
// }}}
|
|
end else if (!o_dma_busy)
|
|
begin
|
|
// {{{
|
|
o_dma_busy <= 1'b0;
|
|
|
|
r_length <= 0;
|
|
// Verilator lint_off WIDTH
|
|
r_transferlen <= (i_length < i_transferlen) ? i_length
|
|
: i_transferlen;
|
|
// Verilator lint_on WIDTH
|
|
|
|
fsm_state <= S_IDLE;
|
|
|
|
o_mm2s_request <= 0;
|
|
o_s2mm_request <= 0;
|
|
|
|
if (i_dma_request)
|
|
begin
|
|
o_dma_busy <= 1'b1;
|
|
|
|
fsm_state <= (i_trigger) ? S_READ : S_WAIT;
|
|
o_mm2s_request <= i_trigger;
|
|
|
|
o_mm2s_addr <= i_src_addr;
|
|
o_s2mm_addr <= i_dst_addr;
|
|
r_length <= i_length;
|
|
end
|
|
`ifdef FORMAL
|
|
assert(fsm_state == S_IDLE);
|
|
`endif
|
|
// }}}
|
|
end else case(fsm_state)
|
|
S_WAIT: begin
|
|
// {{{
|
|
if (r_length == 0)
|
|
o_dma_busy <= 0;
|
|
else if (i_trigger)
|
|
begin
|
|
fsm_state <= S_READ;
|
|
o_mm2s_request <= 1'b1;
|
|
end end
|
|
// }}}
|
|
S_READ: begin
|
|
// {{{
|
|
if (o_mm2s_request && !i_mm2s_busy) // VALID && READY
|
|
o_mm2s_request <= 1'b0;
|
|
if (!i_mm2s_busy && !o_mm2s_request)
|
|
begin
|
|
fsm_state <= S_WRITE;
|
|
o_s2mm_request <= 1'b1;
|
|
|
|
if (i_mm2s_inc)
|
|
// Verilator lint_off WIDTH
|
|
o_mm2s_addr <= o_mm2s_addr + r_transferlen;
|
|
// Verilator lint_on WIDTH
|
|
|
|
// Verilator lint_off WIDTH
|
|
r_length <= (r_length > r_transferlen)
|
|
? r_length - r_transferlen : 0;
|
|
// Verilator lint_on WIDTH
|
|
end end
|
|
// }}}
|
|
S_WRITE: begin
|
|
// {{{
|
|
if (o_s2mm_request && !i_s2mm_busy) // VALID && READY
|
|
o_s2mm_request <= 1'b0;
|
|
if (!i_s2mm_busy && !o_s2mm_request)
|
|
begin
|
|
fsm_state <= (i_trigger) ? S_READ : S_WAIT;
|
|
o_mm2s_request <= (i_trigger);
|
|
|
|
// Verilator lint_off WIDTH
|
|
if (r_transferlen > r_length)
|
|
// Verilator lint_on WIDTH
|
|
r_transferlen <= r_length[LGSUBLENGTH:0];
|
|
|
|
if (i_s2mm_inc)
|
|
// Verilator lint_off WIDTH
|
|
o_s2mm_addr <= o_s2mm_addr + r_transferlen;
|
|
// Verilator lint_on WIDTH
|
|
|
|
if (r_length == 0)
|
|
begin
|
|
fsm_state <= S_IDLE;
|
|
o_mm2s_request <= 1'b0;
|
|
o_dma_busy <= 1'b0;
|
|
end
|
|
end end
|
|
// }}}
|
|
// Verilator coverage_off
|
|
default: begin end
|
|
// Verilator coverage_on
|
|
endcase
|
|
|
|
assign o_s2mm_transferlen = r_transferlen;
|
|
assign o_mm2s_transferlen = r_transferlen;
|
|
assign o_remaining_len = r_length;
|
|
|
|
// o_dma_err
|
|
// {{{
|
|
always @(posedge i_clk)
|
|
if (i_reset || i_soft_reset || !o_dma_busy)
|
|
o_dma_err <= 1'b0;
|
|
else
|
|
o_dma_err <= (i_mm2s_busy && i_mm2s_err)
|
|
|| (i_s2mm_busy && i_s2mm_err);
|
|
// }}}
|
|
endmodule
|