mirror of https://github.com/zxdos/zxuno.git
309 lines
10 KiB
Verilog
309 lines
10 KiB
Verilog
`timescale 1ns / 1ps
|
||
`default_nettype none
|
||
|
||
// This file is part of the ZXUNO Spectrum core.
|
||
// Creation date is 03:02:46 2017-02-13 by Miguel Angel Rodriguez Jodar
|
||
// (c)2014-2020 ZXUNO association.
|
||
// ZXUNO official repository: http://svn.zxuno.com/svn/zxuno
|
||
// Username: guest Password: zxuno
|
||
// Github repository for this core: https://github.com/mcleod-ideafix/zxuno_spectrum_core
|
||
//
|
||
// ZXUNO Spectrum core is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU General Public License as published by
|
||
// the Free Software Foundation, either version 3 of the License, or
|
||
// (at your option) any later version.
|
||
//
|
||
// ZXUNO Spectrum core is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
// GNU General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU General Public License
|
||
// along with the ZXUNO Spectrum core. If not, see <https://www.gnu.org/licenses/>.
|
||
//
|
||
// Any distributed copy of this file must keep this notice intact.
|
||
|
||
module dma (
|
||
input wire clk,
|
||
input wire rst_n,
|
||
input wire [7:0] zxuno_addr,
|
||
input wire regaddr_changed,
|
||
input wire zxuno_regrd,
|
||
input wire zxuno_regwr,
|
||
input wire [7:0] din,
|
||
output reg [7:0] dout,
|
||
output reg oe,
|
||
//---- DMA bus -----
|
||
input wire m1_n,
|
||
output reg busrq_n,
|
||
input wire busak_n,
|
||
output reg [15:0] dma_a,
|
||
input wire [7:0] dma_din,
|
||
output reg [7:0] dma_dout,
|
||
output reg dma_mreq_n,
|
||
output reg dma_iorq_n,
|
||
output reg dma_rd_n,
|
||
output reg dma_wr_n
|
||
);
|
||
|
||
`include "config.vh"
|
||
|
||
localparam
|
||
NODMA = 3'd0,
|
||
DOBURST = 3'd1,
|
||
DOTIMED = 3'd2,
|
||
DOTIMED_2 = 3'd3,
|
||
DOTRANSFER = 3'd4,
|
||
TRANSFER_2 = 3'd5,
|
||
TRANSFER_3 = 3'd6;
|
||
|
||
reg [2:0] iocnt = 3'b000;
|
||
reg [1:0] mode = 2'b00; // 00: apagado, 01: burst sin reinicio, 10: timed, sin reinicio, 11: timed, con reinicio
|
||
reg [1:0] srcdst = 2'b00; // 00: memoria a memoria, 01: memoria a I/O, 10: I/O a memoria, 11: I/O a I/O
|
||
reg select_addr_to_reach = 1'b0; // 0: el bit 7 de DMASTAT obedece a la direccion fuente, 1: obedece a la direcci<63>n destino
|
||
reg [15:0] src = 16'h0000, dst = 16'h0000;
|
||
reg [15:0] srcidx = 16'h0000, dstidx = 16'h0000;
|
||
reg [15:0] preescaler = 16'h0000;
|
||
reg [15:0] cntpreescaler = 16'h0000;
|
||
reg [15:0] transferlength = 16'h0000;
|
||
reg [15:0] cnttransfers = 16'h0000;
|
||
reg [15:0] addrtoreach = 16'h0000;
|
||
reg [2:0] state = NODMA;
|
||
reg [2:0] returnstate = NODMA;
|
||
reg [7:0] data;
|
||
reg data_received = 1'b0;
|
||
reg addr_is_reached = 1'b0;
|
||
reg hilo = 1'b0; // flipflop para determinar qu<71> cacho de dato se va a dar en una lectura
|
||
reg read_in_progress = 1'b0;
|
||
initial busrq_n = 1'b1;
|
||
initial dma_mreq_n = 1'b1;
|
||
initial dma_iorq_n = 1'b1;
|
||
initial dma_rd_n = 1'b1;
|
||
initial dma_wr_n = 1'b1;
|
||
|
||
// CPU reads DMA registers
|
||
always @* begin
|
||
oe = 1'b0;
|
||
dout = 8'hFF;
|
||
if (zxuno_addr == DMACTRL && zxuno_regrd == 1'b1) begin
|
||
dout = {3'b000, select_addr_to_reach, srcdst, mode};
|
||
oe = 1'b1;
|
||
end
|
||
else if (zxuno_addr == DMASTAT && zxuno_regrd == 1'b1) begin
|
||
dout = {addr_is_reached,7'b0000000};
|
||
oe = 1'b1;
|
||
end
|
||
else if (zxuno_addr == DMASRC && zxuno_regrd == 1'b1) begin
|
||
dout = (hilo)? src[15:8]:src[7:0];
|
||
oe = 1'b1;
|
||
end
|
||
else if (zxuno_addr == DMADST && zxuno_regrd == 1'b1) begin
|
||
dout = (hilo)? dst[15:8]:dst[7:0];
|
||
oe = 1'b1;
|
||
end
|
||
else if (zxuno_addr == DMAPRE && zxuno_regrd == 1'b1) begin
|
||
dout = (hilo)? preescaler[15:8]:preescaler[7:0];
|
||
oe = 1'b1;
|
||
end
|
||
else if (zxuno_addr == DMALEN && zxuno_regrd == 1'b1) begin
|
||
dout = (hilo)? transferlength[15:8]:transferlength[7:0];
|
||
oe = 1'b1;
|
||
end
|
||
else if (zxuno_addr == DMAPROB && zxuno_regrd == 1'b1) begin
|
||
dout = (hilo)? addrtoreach[15:8]:addrtoreach[7:0];
|
||
oe = 1'b1;
|
||
end
|
||
end
|
||
|
||
// CPU writes DMA registers
|
||
always @(posedge clk) begin
|
||
iocnt <= iocnt + 3'd1; // free running 3-bit counter
|
||
if (rst_n == 1'b0) begin
|
||
data_received <= 1'b0;
|
||
mode <= 2'b00;
|
||
srcdst <= 2'b00;
|
||
select_addr_to_reach <= 1'b0;
|
||
preescaler <= 16'h0000;
|
||
transferlength <= 16'h0000;
|
||
addrtoreach <= 16'h0000;
|
||
src <= 16'h0000;
|
||
dst <= 16'h0000;
|
||
hilo <= 1'b0;
|
||
read_in_progress <= 1'b0;
|
||
end
|
||
else begin
|
||
if (regaddr_changed == 1'b1) begin
|
||
hilo <= 1'b0;
|
||
read_in_progress <= 1'b0;
|
||
end
|
||
else if (zxuno_regrd == 1'b1 && (zxuno_addr == DMASRC || zxuno_addr == DMADST || zxuno_addr == DMAPRE || zxuno_addr == DMALEN || zxuno_addr == DMAPROB || zxuno_addr == DMASTAT))
|
||
read_in_progress <= 1'b1;
|
||
else if (read_in_progress == 1'b1 && zxuno_regrd == 1'b0) begin
|
||
hilo <= ~hilo;
|
||
read_in_progress <= 1'b0;
|
||
if (zxuno_addr == DMASTAT)
|
||
addr_is_reached <= 1'b0; // resetear direccion alcanzada despu<70>s de haber sido leido
|
||
end
|
||
if (zxuno_addr == DMACTRL && zxuno_regwr == 1'b1)
|
||
{select_addr_to_reach,srcdst,mode} <= din[4:0];
|
||
else if (zxuno_regwr == 1'b1 && (zxuno_addr == DMASRC || zxuno_addr == DMADST || zxuno_addr == DMAPRE || zxuno_addr == DMALEN || zxuno_addr == DMAPROB)) begin
|
||
data_received <= 1'b1;
|
||
data <= din;
|
||
end
|
||
else if (data_received == 1'b1 && zxuno_regwr == 1'b0) begin // just after the I/O write operation has finished, 16-bit registers are updated
|
||
case (zxuno_addr)
|
||
DMASRC : src <= {data, src[15:8]};
|
||
DMADST : dst <= {data, dst[15:8]};
|
||
DMAPRE : preescaler <= {data, preescaler[15:8]};
|
||
DMALEN : transferlength <= {data, transferlength[15:8]};
|
||
DMAPROB: addrtoreach <= {data, addrtoreach[15:8]};
|
||
endcase
|
||
data_received <= 1'b0;
|
||
end
|
||
end
|
||
|
||
// DMA FSM
|
||
if (rst_n == 1'b0) begin
|
||
busrq_n <= 1'b1;
|
||
dma_mreq_n <= 1'b1;
|
||
dma_iorq_n <= 1'b1;
|
||
dma_rd_n <= 1'b1;
|
||
dma_wr_n <= 1'b1;
|
||
cntpreescaler <= 16'h0000;
|
||
cnttransfers <= 16'h0000;
|
||
state <= NODMA;
|
||
end
|
||
else begin
|
||
if (srcdst == 2'b00 || iocnt == 3'b000) begin
|
||
if (cntpreescaler == 16'h0000)
|
||
cntpreescaler <= preescaler;
|
||
else
|
||
cntpreescaler <= cntpreescaler + 16'hFFFF ; // -1
|
||
case (state)
|
||
NODMA:
|
||
begin
|
||
if (mode == 2'b01/* && m1_n == 1'b0*/) begin
|
||
state <= DOBURST;
|
||
srcidx <= src;
|
||
dstidx <= dst;
|
||
busrq_n <= 1'b0;
|
||
cnttransfers <= transferlength;
|
||
end
|
||
else if (mode == 2'b10 || mode == 2'b11) begin
|
||
state <= DOTIMED;
|
||
srcidx <= src;
|
||
dstidx <= dst;
|
||
cntpreescaler <= preescaler;
|
||
cnttransfers <= transferlength;
|
||
end
|
||
else
|
||
busrq_n <= 1'b1;
|
||
end
|
||
|
||
DOBURST:
|
||
begin
|
||
if (busak_n == 1'b0) begin
|
||
if (cnttransfers == 16'h0000) begin
|
||
state <= NODMA;
|
||
mode <= 2'b00; // clear transfer mode
|
||
busrq_n <= 1'b1;
|
||
end
|
||
else begin
|
||
state <= DOTRANSFER;
|
||
dma_a <= srcidx;
|
||
if (srcdst[1] == 1'b0)
|
||
dma_mreq_n <= 1'b0;
|
||
else
|
||
dma_iorq_n <= 1'b0;
|
||
dma_rd_n <= 1'b0;
|
||
returnstate <= DOBURST;
|
||
end
|
||
end
|
||
end
|
||
|
||
DOTIMED:
|
||
begin
|
||
if (mode == 2'b00) begin
|
||
state <= NODMA;
|
||
busrq_n <= 1'b1;
|
||
end
|
||
else if (cntpreescaler == 16'h0000) begin
|
||
busrq_n <= 1'b0;
|
||
state <= DOTIMED_2;
|
||
end
|
||
else
|
||
busrq_n <= 1'b1;
|
||
end
|
||
|
||
DOTIMED_2:
|
||
begin
|
||
if (busak_n == 1'b0) begin
|
||
if (cnttransfers != 16'h0000) begin
|
||
state <= DOTRANSFER;
|
||
dma_a <= srcidx;
|
||
if (srcdst[1] == 1'b0)
|
||
dma_mreq_n <= 1'b0;
|
||
else
|
||
dma_iorq_n <= 1'b0;
|
||
dma_rd_n <= 1'b0;
|
||
returnstate <= DOTIMED;
|
||
end
|
||
else if (mode == 2'b11) begin
|
||
cnttransfers <= transferlength; // reiniciar timed con reinicio
|
||
srcidx <= src;
|
||
dstidx <= dst;
|
||
end
|
||
else begin
|
||
mode <= 2'b00; // fin de timed sin reinicio
|
||
busrq_n <= 1'b1;
|
||
state <= NODMA;
|
||
end
|
||
end
|
||
end
|
||
|
||
//--- One transfer ---
|
||
DOTRANSFER:
|
||
begin
|
||
dma_dout <= dma_din;
|
||
dma_rd_n <= 1'b1;
|
||
dma_mreq_n <= 1'b1;
|
||
dma_iorq_n <= 1'b1;
|
||
dma_a <= dstidx;
|
||
state <= TRANSFER_2;
|
||
if ((select_addr_to_reach == 1'b0 && srcidx == addrtoreach) || (select_addr_to_reach == 1'b1 && dstidx == addrtoreach))
|
||
addr_is_reached <= 1'b1;
|
||
end
|
||
|
||
TRANSFER_2:
|
||
begin
|
||
if (srcdst[0] == 1'b0) begin
|
||
dma_mreq_n <= 1'b0;
|
||
end
|
||
else begin
|
||
dma_iorq_n <= 1'b0;
|
||
end
|
||
dma_wr_n <= 1'b0;
|
||
state <= TRANSFER_3;
|
||
end
|
||
|
||
TRANSFER_3:
|
||
begin
|
||
dma_mreq_n <= 1'b1;
|
||
dma_iorq_n <= 1'b1;
|
||
dma_wr_n <= 1'b1;
|
||
cnttransfers <= cnttransfers + 16'hFFFF; // -1
|
||
if (srcdst[1] == 1'b0)
|
||
srcidx <= srcidx + 16'd1;
|
||
if (srcdst[0] == 1'b0)
|
||
dstidx <= dstidx + 16'd1;
|
||
state <= returnstate;
|
||
end
|
||
|
||
default:
|
||
state <= NODMA;
|
||
endcase
|
||
end
|
||
end
|
||
end
|
||
endmodule
|