// part of NeoGS project
//
// (c) NedoPC 2007-2008
//
// modelling is in tb_dma1.*
// look also at dma_access.png
module dma_access(
input clk,
input rst_n,
input dma_req, // DMA request
input [20:0] dma_addr, // DMA address (2mb)
input dma_rnw, // DMA READ/nWRITE
input [7:0] dma_wd, // DMA data to write
output reg [7:0] dma_rd, // DMA data just read
output reg dma_busynready, // DMA BUSY/nREADY
output reg dma_ack, // positive pulse as dma_busynready goes high
output reg dma_end, // positive pulse as dma_busynready goes low
output wire mem_dma_bus, // DMA taking over the bus
output wire [20:0] mem_dma_addr, // DMA address going to the bus
output wire [7:0] mem_dma_wd, // DMA data going to the bus
input [7:0] mem_dma_rd, // DMA data going from the bus
output wire mem_dma_rnw, // DMA bus direction (1=read, 0=write)
output reg mem_dma_oe, // DMA read strobe going to the bus
output reg mem_dma_we, // DMA write pulse going to the bus
output reg busrq_n, // CPU signals
input busak_n // control
);
reg dma_bus;
reg [20:0] int_dma_addr;
reg int_dma_rnw;
reg [7:0] int_dma_wd;
wire [7:0] int_dma_rd;
assign mem_dma_bus = dma_bus;
assign mem_dma_addr = int_dma_addr;
assign mem_dma_wd = int_dma_wd;
assign mem_dma_rnw = int_dma_rnw;
assign int_dma_rd = mem_dma_rd;
localparam IDLE = 0;
localparam START = 1;
localparam WACK = 2;
localparam READ1 = 3;
localparam READ2 = 4;
localparam WRITE1 = 5;
localparam WRITE2 = 6;
reg [3:0] state;
reg [3:0] next_state;
// for simulation purposes
initial
begin
state <= IDLE;
busrq_n <= 1'b1;
mem_dma_oe <= 1'b1;
mem_dma_we <= 1'b1;
end
// FSM
always @(posedge clk, negedge rst_n)
begin
if( !rst_n )
state <= IDLE;
else
state <= next_state;
end
always @*
begin
case( state )
//////////////////////////////////////////////////////////////////////////////////////////
IDLE:
begin
if( dma_req==1'b1 )
next_state <= START;
else
next_state <= IDLE;
end
//////////////////////////////////////////////////////////////////////////////////////////
START:
begin
next_state <= WACK;
end
//////////////////////////////////////////////////////////////////////////////////////////
WACK:
begin
if( busak_n == 1'b1 ) ///// ACHTUNG WARNING!!! probably use here registered busak?
next_state <= WACK;
else // busak_n == 1'b0
begin
if( int_dma_rnw == 1'b1 ) // read
next_state <= READ1;
else // int_dma_rnw == 1'b0 - write
next_state <= WRITE1;
end
end
//////////////////////////////////////////////////////////////////////////////////////////
READ1:
begin
next_state <= READ2;
end
//////////////////////////////////////////////////////////////////////////////////////////
READ2:
begin
if( dma_req == 1'b0 )
next_state <= IDLE;
else // dma_req == 1'b1
begin
if( dma_rnw == 1'b1 ) // next is read
next_state <= READ1;
else // dma_rnw == 1'b0 - next is write
next_state <= WRITE1;
end
end
//////////////////////////////////////////////////////////////////////////////////////////
WRITE1:
begin
next_state <= WRITE2;
end
//////////////////////////////////////////////////////////////////////////////////////////
WRITE2:
begin
if( dma_req == 1'b0 )
next_state <= IDLE;
else // dma_req == 1'b1
begin
if( dma_rnw == 1'b1 ) // next is read
next_state <= READ1;
else // dma_rnw == 1'b0 - next is write
next_state <= WRITE1;
end
end
//////////////////////////////////////////////////////////////////////////////////////////
endcase
end
always @(posedge clk, negedge rst_n)
begin
if( !rst_n )
begin
busrq_n <= 1'b1;
dma_busynready <= 1'b0;
dma_ack <= 1'b0;
dma_end <= 1'b0;
dma_bus <= 1'b0;
mem_dma_oe <= 1'b1;
end
else case( next_state )
//////////////////////////////////////////////////////////////////////////////////////////
IDLE:
begin
dma_end <= 1'b0;
busrq_n <= 1'b1;
dma_bus <= 1'b0;
mem_dma_oe <= 1'b1;
end
//////////////////////////////////////////////////////////////////////////////////////////
START:
begin
// dma_bus <= 1'b0; // if rst=0>1 and dma_ack=1 --> ??? is this really needed?
busrq_n <= 1'b0;
dma_busynready <= 1'b1;
dma_ack <= 1'b1;
int_dma_rnw <= dma_rnw;
int_dma_addr <= dma_addr;
int_dma_wd <= dma_wd;
end
//////////////////////////////////////////////////////////////////////////////////////////
WACK:
begin
dma_ack <= 1'b0;
end
//////////////////////////////////////////////////////////////////////////////////////////
READ1:
begin
dma_bus <= 1'b1; // take over the bus
mem_dma_oe <= 1'b0;
if( dma_busynready == 1'b0 ) // if we are here from READ2 or WRITE2
begin
dma_busynready <= 1'b1;
dma_ack <= 1'b1;
dma_end <= 1'b0;
int_dma_rnw <= 1'b1;
int_dma_addr <= dma_addr;
end
end
//////////////////////////////////////////////////////////////////////////////////////////
READ2:
begin
dma_busynready <= 1'b0;
dma_ack <= 1'b0;
dma_end <= 1'b1;
dma_rd <= int_dma_rd;
end
//////////////////////////////////////////////////////////////////////////////////////////
WRITE1:
begin
dma_bus <= 1'b1; // take over the bus
mem_dma_oe <= 1'b1;
if( dma_busynready == 1'b0 ) // from READ2 or WRITE2
begin
dma_busynready <= 1'b1;
dma_ack <= 1'b1;
dma_end <= 1'b0;
int_dma_rnw <= 1'b0;
int_dma_addr <= dma_addr;
int_dma_wd <= dma_wd;
end
end
//////////////////////////////////////////////////////////////////////////////////////////
WRITE2:
begin
dma_busynready <= 1'b0;
dma_ack <= 1'b0;
dma_end <= 1'b1;
end
//////////////////////////////////////////////////////////////////////////////////////////
endcase
end
// mem_dma_we generator
always @(negedge clk,negedge rst_n)
begin
if( !rst_n )
mem_dma_we <= 1'b1;
else
begin
if( dma_bus )
begin
if( !int_dma_rnw )
mem_dma_we <= ~mem_dma_we;
end
else
mem_dma_we <= 1'b1;
end
end
endmodule