SummerCart64/fw/rtl/cpu/cpu_dma.sv

223 lines
6.8 KiB
Systemverilog

interface if_dma ();
localparam [1:0] NUM_DEVICES = sc64::__ID_DMA_END;
sc64::e_dma_id id;
logic rx_empty;
logic rx_read;
logic [7:0] rx_rdata;
logic tx_full;
logic tx_write;
logic [7:0] tx_wdata;
logic request;
logic ack;
logic write;
logic [31:0] address;
logic [15:0] rdata;
logic [15:0] wdata;
modport controller (
output id,
input rx_empty,
output rx_read,
input rx_rdata,
input tx_full,
output tx_write,
output tx_wdata,
output request,
input ack,
output write,
output address,
input rdata,
output wdata
);
modport memory (
input request,
output ack,
input write,
input address,
output rdata,
input wdata
);
logic [7:0] device_rx_rdata [(NUM_DEVICES - 1):0];
logic device_rx_empty [(NUM_DEVICES - 1):0];
logic device_tx_full [(NUM_DEVICES - 1):0];
always_comb begin
rx_rdata = 8'd0;
rx_empty = 1'b0;
tx_full = 1'b0;
for (integer i = 0; i < NUM_DEVICES; i++) begin
rx_rdata = rx_rdata | (id == i[1:0] ? device_rx_rdata[i] : 8'd0);
rx_empty = rx_empty | (device_rx_empty[i] && id == i[1:0]);
tx_full = tx_full | (device_tx_full[i] && id == i[1:0]);
end
end
genvar n;
generate
for (n = 0; n < NUM_DEVICES; n++) begin : at
logic device_selected;
logic device_rx_read;
logic device_tx_write;
always_comb begin
device_selected = id == n[1:0];
device_rx_read = device_selected && rx_read;
device_tx_write = device_selected && tx_write;
end
modport device (
output .rx_empty(device_rx_empty[n]),
input .rx_read(device_rx_read),
output .rx_rdata(device_rx_rdata[n]),
output .tx_full(device_tx_full[n]),
input .tx_write(device_tx_write),
input .tx_wdata(tx_wdata)
);
end
endgenerate
endinterface
module cpu_dma (
if_system.sys sys,
if_cpu_bus bus,
if_dma.controller dma
);
typedef enum bit [2:0] {
S_IDLE,
S_FETCH,
S_TRANSFER
} e_state;
e_state state;
logic pending_stop;
logic direction;
logic [27:0] length;
logic [15:0] rdata_buffer;
logic byte_counter;
always_comb begin
bus.rdata = 32'd0;
if (bus.ack) begin
case (bus.address[3:2])
0: bus.rdata = {28'd0, state != S_IDLE, direction, 2'b00};
1: bus.rdata = dma.address;
2: bus.rdata = {2'b00, dma.id, length};
default: bus.rdata = 32'd0;
endcase
end
end
always_ff @(posedge sys.clk) begin
bus.ack <= 1'b0;
if (bus.request) begin
bus.ack <= 1'b1;
end
dma.rx_read <= 1'b0;
dma.tx_write <= 1'b0;
if (sys.reset) begin
state <= S_IDLE;
pending_stop <= 1'b0;
dma.request <= 1'b0;
end else begin
if (bus.request && bus.address[3:2] == 0 && bus.wmask[0]) begin
pending_stop <= bus.wdata[1];
end
case (state)
S_IDLE: begin
pending_stop <= 1'b0;
if (bus.request) begin
case (bus.address[3:2])
0: if (bus.wmask[0]) begin
direction <= bus.wdata[2];
if (bus.wdata[0]) begin
state <= S_FETCH;
byte_counter <= 1'b0;
end
end
1: if (&bus.wmask) begin
dma.address <= bus.wdata;
end
2: if (&bus.wmask) begin
{dma.id, length} <= {bus.wdata[29:1], 1'b0};
end
endcase
end
end
S_FETCH: begin
if (length != 28'd0 && !pending_stop) begin
if (direction) begin
if (!dma.rx_empty && !dma.rx_read) begin
dma.rx_read <= 1'b1;
dma.wdata <= {dma.wdata[7:0], dma.rx_rdata};
byte_counter <= ~byte_counter;
if (byte_counter) begin
state <= S_TRANSFER;
dma.request <= 1'b1;
dma.write <= 1'b1;
end
end
end else begin
dma.request <= 1'b1;
dma.write <= 1'b0;
if (dma.ack) begin
state <= S_TRANSFER;
dma.request <= 1'b0;
rdata_buffer <= dma.rdata;
end
end
end else begin
state <= S_IDLE;
end
end
S_TRANSFER: begin
if (direction) begin
if (dma.ack) begin
state <= S_FETCH;
dma.request <= 1'b0;
dma.address <= dma.address + 2'd2;
length <= length - 2'd2;
end
end else begin
if (!dma.tx_full && !dma.tx_write) begin
dma.tx_write <= 1'b1;
dma.tx_wdata <= byte_counter ? rdata_buffer[7:0] : rdata_buffer[15:8];
byte_counter <= ~byte_counter;
if (byte_counter) begin
state <= S_FETCH;
dma.address <= dma.address + 2'd2;
length <= length - 2'd2;
end
end
end
end
default: begin
state <= S_IDLE;
dma.request <= 1'b0;
end
endcase
end
end
endmodule