blob: 2b0d924e010107f20c3fab3c766080934a37c38b [file] [log] [blame]
`timescale 1ns / 1ps
/**
* Block transfer unit for the GB80 CPU.
*
* Original Author: Joseph Carlos (jdcarlos1@gmail.com)
* Modified: Wenting Zhang (zephray@outlook.com)
*/
/**
* The DMA unit.
*
* Contains the DMA register and performs DMA transfers when the register is
* written to. Each transfer takes 320 cycles rather than the canon 640, this
* is because there's no reason to take 640.
*
* @inout addr_ext The address bus.
* @inout data_ext The data bus.
* @output dma_transfer 1 if a transfer is occurring, 0 otherwise.
* @input mem_re 1 if the processor is reading from memory.
* @input mem_we 1 if the processor is writing to memory.
* @input clock The CPU clock.
* @input reset The CPU reset.
*/
module dma(
input wire clk,
//input wire phi,
input wire rst,
input wire [1:0] ct,
output reg dma_rd,
output reg dma_wr,
//output wire dma_rd_comb,
//output wire dma_wr_comb,
output reg [15:0] dma_a,
input wire [7:0] dma_din,
output reg [7:0] dma_dout,
input wire mmio_wr,
input wire [7:0] mmio_din,
output wire [7:0] mmio_dout,
output wire dma_occupy_bus
);
// DMA data blocks /////////////////////////////////////////////////////////
reg [7:0] dma_start_addr;
reg [7:0] count;
assign mmio_dout = dma_start_addr;
reg cpu_mem_disable;
assign dma_occupy_bus = cpu_mem_disable;
// DMA transfer logic //////////////////////////////////////////////////////
localparam DMA_IDLE = 'd0;
localparam DMA_TRANSFER_READ_ADDR = 'd1;
localparam DMA_TRANSFER_READ_DATA = 'd2;
localparam DMA_TRANSFER_WRITE_DATA = 'd3;
localparam DMA_TRANSFER_WRITE_WAIT = 'd4;
localparam DMA_DELAY = 'd5;
reg [2:0] state;
always @(posedge clk) begin
if (rst) begin
dma_start_addr <= 8'h00;
end
else begin
if (mmio_wr) begin
// Writing is always valid regardless of the state
dma_start_addr <= mmio_din;
end
end
end
always @(posedge clk) begin
if (rst) begin
state <= DMA_IDLE;
count <= 8'd0;
dma_wr <= 1'b0;
dma_rd <= 1'b0;
cpu_mem_disable <= 1'b0;
end
else begin
case (state)
DMA_IDLE: begin
dma_wr <= 1'b0;
dma_rd <= 1'b0;
cpu_mem_disable <= 1'b0;
if (mmio_wr) begin
// Transfer starts on next cycle
state <= DMA_DELAY;
end
count <= 8'd0;
end
DMA_DELAY: begin
if (ct == 2'b10) begin
state <= DMA_TRANSFER_WRITE_WAIT;
end
end
DMA_TRANSFER_READ_ADDR: begin
if (mmio_wr) begin // Allow re-triggering
state <= DMA_DELAY;
count <= 8'd0;
end
else
state <= DMA_TRANSFER_READ_DATA;
end
DMA_TRANSFER_READ_DATA: begin
state <= DMA_TRANSFER_WRITE_DATA;
// Basically wait
end
DMA_TRANSFER_WRITE_DATA: begin
// Read data
dma_dout <= dma_din;
dma_rd <= 1'b0;
// Write the temp register to memory
dma_a <= {8'hfe, count}; // Output write address
dma_wr <= 1'b1;
count <= count + 8'd1;
if (mmio_wr) begin // Allow re-triggering
state <= DMA_DELAY;
count <= 8'd0;
end
else
state <= DMA_TRANSFER_WRITE_WAIT;
end
DMA_TRANSFER_WRITE_WAIT: begin
// Wait
if (mmio_wr) begin // Allow re-triggering
state <= DMA_DELAY;
count <= 8'd0; // Delay before start
end
else
if (count == 8'h9f) begin
state <= DMA_IDLE;
cpu_mem_disable <= 1'b0;
count <= 8'd0;
end
else begin
state <= DMA_TRANSFER_READ_ADDR;
// Output address
dma_wr <= 1'b0;
cpu_mem_disable <= 1'b1;
// Load the temp register with data from memory
dma_a <= {dma_start_addr, count}; // Output read address
dma_rd <= 1'b1;
end
end
default: begin
end
endcase
end
end
endmodule // dma