SummerCart64/fw/rtl/memory/memory_dma.sv

238 lines
7.8 KiB
Systemverilog
Raw Normal View History

2022-02-02 19:07:43 +01:00
interface if_memory_dma ();
logic request;
logic ack;
logic write;
logic [1:0] wmask;
logic [31:0] address;
logic [15:0] rdata;
logic [15:0] wdata;
logic start;
logic stop;
logic busy;
logic direction;
logic [31:0] transfer_length;
logic [31:0] starting_address;
logic dma_rx_read;
logic dma_tx_write;
logic [7:0] dma_tx_wdata;
logic cpu_rx_read;
logic cpu_tx_write;
logic [7:0] cpu_tx_wdata;
logic rx_empty;
logic rx_almost_empty;
logic rx_read;
logic [7:0] rx_rdata;
logic tx_full;
logic tx_almost_full;
logic tx_write;
logic [7:0] tx_wdata;
always_comb begin
rx_read = dma_rx_read || cpu_rx_read;
tx_write = dma_tx_write || cpu_tx_write;
tx_wdata = cpu_tx_write ? cpu_tx_wdata : dma_tx_wdata;
end
modport dma (
input start,
input stop,
output busy,
input direction,
input transfer_length,
input starting_address,
output request,
input ack,
output write,
output wmask,
output address,
input rdata,
output wdata,
input rx_empty,
input rx_almost_empty,
output dma_rx_read,
input rx_rdata,
input tx_full,
input tx_almost_full,
output dma_tx_write,
output dma_tx_wdata
);
modport cpu (
output start,
output stop,
input busy,
output direction,
output transfer_length,
output starting_address,
input rx_empty,
output cpu_rx_read,
input rx_rdata,
input tx_full,
output cpu_tx_write,
output cpu_tx_wdata
);
modport memory (
input request,
output ack,
input write,
input wmask,
input address,
output rdata,
input wdata
);
modport device (
output rx_empty,
output rx_almost_empty,
input rx_read,
output rx_rdata,
output tx_full,
output tx_almost_full,
input tx_write,
input tx_wdata
);
endinterface
module memory_dma (
input clk,
input reset,
if_memory_dma.dma dma
);
typedef enum bit [0:0] {
STATE_FETCH,
STATE_TRANSFER
} e_state;
logic [31:0] remaining;
logic [15:0] data_buffer;
logic byte_counter;
e_state state;
always_ff @(posedge clk) begin
dma.dma_rx_read <= 1'b0;
dma.dma_tx_write <= 1'b0;
if (dma.dma_rx_read) begin
if (dma.address[0] || (remaining == 32'd1)) begin
dma.wdata <= {dma.rx_rdata, dma.rx_rdata};
end else begin
dma.wdata <= {dma.wdata[7:0], dma.rx_rdata};
end
end
if (reset) begin
dma.busy <= 1'b0;
dma.request <= 1'b0;
end else begin
if (!dma.busy) begin
if (dma.start) begin
dma.busy <= 1'b1;
dma.write <= dma.direction;
dma.address <= dma.starting_address;
remaining <= dma.transfer_length;
byte_counter <= 1'd0;
state <= STATE_FETCH;
end
end else begin
if (dma.stop) begin
dma.busy <= 1'b0;
dma.request <= 1'b0;
end else if (remaining != 32'd0) begin
if (dma.write) begin
case (state)
STATE_FETCH: begin
if (!dma.rx_empty && !(dma.dma_rx_read && dma.rx_almost_empty)) begin
dma.dma_rx_read <= 1'b1;
if (dma.address[0]) begin
dma.wmask <= 2'b01;
state <= STATE_TRANSFER;
end else if (remaining == 32'd1) begin
dma.wmask <= 2'b10;
state <= STATE_TRANSFER;
end else begin
byte_counter <= byte_counter + 1'd1;
if (byte_counter) begin
dma.wmask <= 2'b11;
state <= STATE_TRANSFER;
end
end
end
end
STATE_TRANSFER: begin
dma.request <= 1'b1;
if (dma.ack) begin
dma.request <= 1'b0;
if (dma.wmask != 2'b11) begin
dma.address <= dma.address + 1'd1;
remaining <= remaining - 1'd1;
end else begin
dma.address <= dma.address + 2'd2;
remaining <= remaining - 2'd2;
end
state <= STATE_FETCH;
end
end
endcase
end else begin
case (state)
STATE_FETCH: begin
dma.request <= 1'b1;
if (dma.ack) begin
dma.request <= 1'b0;
data_buffer <= dma.rdata;
state <= STATE_TRANSFER;
end
end
STATE_TRANSFER: begin
if (!dma.tx_full && !(dma.dma_tx_write && dma.tx_almost_full)) begin
dma.dma_tx_write <= 1'b1;
if (dma.address[0]) begin
dma.address <= dma.address + 1'd1;
remaining <= remaining - 1'd1;
dma.dma_tx_wdata <= data_buffer[7:0];
state <= STATE_FETCH;
end else if (remaining == 32'd1) begin
dma.address <= dma.address + 1'd1;
remaining <= remaining - 1'd1;
dma.dma_tx_wdata <= data_buffer[15:8];
state <= STATE_FETCH;
end else begin
dma.dma_tx_wdata <= byte_counter ? data_buffer[7:0] : data_buffer[15:8];
byte_counter <= byte_counter + 1'd1;
if (byte_counter) begin
dma.address <= dma.address + 2'd2;
remaining <= remaining - 2'd2;
state <= STATE_FETCH;
end
end
end
end
endcase
end
end else begin
dma.busy <= 1'b0;
end
end
end
end
endmodule