mirror of
https://github.com/MiSTer-devel/Archie_MiSTer.git
synced 2026-04-19 03:04:04 +00:00
440 lines
12 KiB
Verilog
440 lines
12 KiB
Verilog
`timescale 1ns / 1ps
|
|
/* memc.v
|
|
|
|
Copyright (c) 2012-2014, Stephen J. Leary
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the <organization> nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
module memc
|
|
(
|
|
input clkcpu,
|
|
input rst_i,
|
|
|
|
// cpu bus.
|
|
input cpu_we,
|
|
input cpu_stb,
|
|
input cpu_cyc,
|
|
output cpu_err,
|
|
output cpu_ack,
|
|
output [31:0] cpu_dout,
|
|
|
|
input [25:0] cpu_address,
|
|
input [3:0] cpu_sel,
|
|
|
|
// external memory bus.
|
|
output [23:2] mem_addr_o,
|
|
output mem_stb_o,
|
|
output mem_cyc_o,
|
|
output mem_we_o,
|
|
output [3:0] mem_sel_o,
|
|
input [31:0] mem_dat_i,
|
|
|
|
input mem_ack_i,
|
|
output [2:0] mem_cti_o, // burst / normal
|
|
|
|
// supervisor mode
|
|
|
|
input spvmd,
|
|
|
|
// vidc interface
|
|
input flybk,
|
|
input hsync,
|
|
|
|
input sndrq,
|
|
output sndak,
|
|
|
|
input vidrq,
|
|
output vidak,
|
|
output vidw, // write to the video registers.
|
|
|
|
// ioc interface
|
|
output ioc_cs,
|
|
output rom_low_cs,
|
|
output ram_cs, // accessing ram.
|
|
|
|
// interrupts
|
|
output sirq_n
|
|
);
|
|
|
|
parameter INITIAL_CURSOR_BASE = 19'h0_0000;
|
|
parameter INITIAL_SCREEN_BASE = 19'h0_0000;
|
|
parameter INITIAL_SCREEN_SIZE = 19'h4_b000;
|
|
|
|
reg rom_overlay;
|
|
|
|
reg [18:0] cur_address; // actual
|
|
reg [18:0] cur_init;
|
|
|
|
reg [18:0] vid_address; // actual
|
|
reg [18:0] vid_init;
|
|
reg [18:0] vid_start;
|
|
reg [18:0] vid_end;
|
|
|
|
reg [18:0] snd_sptr; // actual/current
|
|
reg [18:0] snd_endc;
|
|
|
|
reg snd_next_valid;
|
|
reg [18:0] snd_start;
|
|
reg [18:0] snd_endn;
|
|
|
|
reg [13:0] memc_control = 14'd0;
|
|
reg cur_load;
|
|
reg vid_load;
|
|
reg snd_load;
|
|
reg cpu_load;
|
|
|
|
reg [3:0] dma_ack_r;
|
|
wire dma_in_progress = cur_load | vid_load | snd_load;
|
|
wire dma_request = (~flybk & vidrq) | (memc_control[11] & sndrq);
|
|
reg dma_request_r;
|
|
wire video_dma_ip = cur_load | vid_load;
|
|
wire sound_dma_ip = snd_load;
|
|
wire phycs, tablew, romcs, memcw;
|
|
|
|
// register addresses.
|
|
localparam REG_Vinit = 3'b000;
|
|
localparam REG_Vstart = 3'b001;
|
|
localparam REG_Vend = 3'b010;
|
|
localparam REG_Cinit = 3'b011;
|
|
localparam REG_Sstart = 3'b100;
|
|
localparam REG_SendN = 3'b101;
|
|
localparam REG_Sptr = 3'b110;
|
|
localparam REG_Ctrl = 3'b111;
|
|
|
|
wire[25:0] phys_address;
|
|
wire table_valid;
|
|
|
|
memc_translator PAGETABLES
|
|
(
|
|
.clkcpu ( clkcpu ),
|
|
.wr ( tablew ),
|
|
.spvmd ( spvmd ),
|
|
.page_size ( memc_control[3:2] ),
|
|
.osmd ( memc_control[12] ),
|
|
.mem_write ( cpu_we ),
|
|
.addr_i ( cpu_address ),
|
|
.addr_o ( phys_address ),
|
|
.valid ( table_valid )
|
|
);
|
|
|
|
reg [31:0] cache_data[0:3];
|
|
reg cache_valid;
|
|
reg [23:4] cache_addr;
|
|
reg cache_ack;
|
|
|
|
always @(posedge clkcpu) begin : block
|
|
reg cache_rcv, cache_test;
|
|
reg [1:0] cache_cnt;
|
|
reg [1:0] cache_wraddr;
|
|
|
|
cache_ack <= 0;
|
|
|
|
if (rst_i) begin
|
|
|
|
vid_init <= INITIAL_SCREEN_BASE;
|
|
cur_init <= INITIAL_CURSOR_BASE;
|
|
vid_start <= INITIAL_SCREEN_BASE;
|
|
vid_end <= INITIAL_SCREEN_BASE + INITIAL_SCREEN_SIZE;
|
|
vid_address <= INITIAL_SCREEN_BASE;
|
|
cur_address <= INITIAL_CURSOR_BASE;
|
|
|
|
vid_load = 1'b0;
|
|
snd_load = 1'b0;
|
|
cur_load = 1'b0;
|
|
cpu_load <= 1'b0;
|
|
rom_overlay <= 1'b1; // start with rom overlay
|
|
|
|
memc_control[11] <= 1'b0; // disable sound dma on reset.
|
|
|
|
dma_request_r <= 1'b0;
|
|
cache_rcv <= 0;
|
|
cache_valid <= 0;
|
|
cache_test <= 0;
|
|
|
|
snd_next_valid <= 1'b0; // sound init.
|
|
|
|
dma_ack_r = 4'd0; // video init.
|
|
|
|
end else begin
|
|
|
|
if(cache_rcv & mem_ack_i) begin
|
|
cache_data[cache_wraddr] <= mem_dat_i;
|
|
cache_wraddr <= cache_wraddr + 1'd1;
|
|
cache_cnt <= cache_cnt + 1'd1;
|
|
if(cache_cnt == 2) cache_ack <= 1;
|
|
if(&cache_cnt) begin
|
|
cache_rcv <= 0;
|
|
cache_valid <= 1;
|
|
end
|
|
end
|
|
|
|
dma_request_r <= dma_request;
|
|
|
|
// cpu cycle.
|
|
if (cpu_cyc & cpu_stb) begin
|
|
cache_test <= 1;
|
|
if(cache_valid & (cache_addr == caddr[23:4]) & ~cpu_mem_we) begin
|
|
// cache hit
|
|
if(~cache_test) cache_ack <= 1;
|
|
end
|
|
else begin
|
|
// logic to ensure that the rom overlay gets deactivated.
|
|
if (cpu_address[25:24] == 2'b11) begin
|
|
|
|
rom_overlay <= 1'b0;
|
|
|
|
end
|
|
|
|
// ensure no video cycle is active or about to start.
|
|
if (~dma_request_r & ~dma_in_progress) begin
|
|
cpu_load <= 1'b1;
|
|
if(~cpu_load) begin
|
|
if(cpu_mem_we) begin
|
|
if(cache_addr == caddr[23:4]) cache_valid <= 0;
|
|
end
|
|
else begin
|
|
{cache_addr,cache_wraddr} <= caddr[23:2];
|
|
cache_valid <= 0;
|
|
cache_rcv <= 1;
|
|
cache_cnt <= 0;
|
|
end
|
|
end
|
|
end
|
|
|
|
if (memw) begin
|
|
|
|
// load the registers.
|
|
// all the registers are loaded here.
|
|
case (cpu_address[19:17])
|
|
|
|
REG_Vinit: vid_init <= {cpu_address[16:2], 4'b0000};
|
|
REG_Vstart: vid_start <= {cpu_address[16:2], 4'b0000};
|
|
REG_Vend: vid_end <= {cpu_address[16:2], 4'b0000};
|
|
REG_Cinit: cur_init <= {cpu_address[16:2], 4'b0000};
|
|
|
|
REG_Sstart: begin
|
|
$display("Sstart: %x", {cpu_address[16:2], 4'b0000});
|
|
snd_next_valid <= 1'b1;
|
|
snd_start <= {cpu_address[16:2], 4'b0000};
|
|
end
|
|
|
|
REG_SendN: begin
|
|
|
|
$display("SendN: %x", {cpu_address[16:2], 4'b0000});
|
|
snd_endn <= {cpu_address[16:2], 4'b0000};
|
|
|
|
end
|
|
|
|
REG_Sptr: begin
|
|
|
|
$display("Sound buffer swap");
|
|
snd_sptr <= snd_start;
|
|
|
|
if (snd_next_valid == 1'b1) begin
|
|
snd_endc <= snd_endn;
|
|
snd_next_valid <= 1'b0;
|
|
end
|
|
|
|
end
|
|
|
|
REG_Ctrl: begin
|
|
|
|
$display("MEMC Control Register: %x", cpu_address[13:0]);
|
|
memc_control <= cpu_address[13:0];
|
|
|
|
end
|
|
|
|
endcase
|
|
|
|
end
|
|
end
|
|
|
|
end else begin
|
|
|
|
cpu_load <= 0;
|
|
cache_rcv <= 0;
|
|
cache_test <= 0;
|
|
end
|
|
|
|
// video dma stuff.
|
|
if (flybk == 1'b1) begin
|
|
|
|
// stop all video dma on flybk
|
|
vid_address <= vid_init;
|
|
cur_address <= cur_init;
|
|
|
|
if (vid_load | cur_load) begin
|
|
|
|
dma_ack_r <= 4'd0;
|
|
|
|
end
|
|
|
|
vid_load <= 1'b0;
|
|
cur_load <= 1'b0;
|
|
|
|
end
|
|
|
|
// do the dma count for all cycle types.
|
|
if (dma_in_progress & mem_ack_i) begin
|
|
|
|
dma_ack_r <= dma_ack_r + 3'd1;
|
|
|
|
if (dma_ack_r == 4'd3) begin
|
|
|
|
vid_load <= 1'b0;
|
|
snd_load <= 1'b0;
|
|
cur_load <= 1'b0;
|
|
dma_ack_r <= 4'd0;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if (dma_request_r === 1'b1) begin
|
|
|
|
// priority is to video over sound.
|
|
if (vidrq & ~dma_in_progress & ~cpu_load) begin
|
|
|
|
if (hsync == 1'b1) begin
|
|
|
|
vid_load <= 1'b1;
|
|
|
|
end else begin
|
|
|
|
cur_load <= 1'b1;
|
|
|
|
end
|
|
|
|
end else if (sndrq & ~dma_in_progress & ~cpu_load) begin
|
|
|
|
snd_load <= 1'b1;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if (video_dma_ip) begin
|
|
|
|
if ((vidak & vid_load) == 1'b1) begin
|
|
|
|
// advance the pointer to the next location.
|
|
vid_address <= vid_address + 19'd4;
|
|
|
|
end else if ((vidak & cur_load) == 1'b1) begin
|
|
|
|
// advance the cursor pointer to the next location.
|
|
cur_address <= cur_address + 19'd4;
|
|
|
|
end
|
|
|
|
end else begin
|
|
|
|
// cant wrap during a burst
|
|
if (vid_address > vid_end) begin
|
|
|
|
// loop back to vid_start when we reach the end.
|
|
vid_address <= vid_start;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if (sound_dma_ip) begin
|
|
|
|
if ((sndak & snd_load) == 1'b1) begin
|
|
|
|
// advance the pointer to the next location.
|
|
snd_sptr <= snd_sptr + 19'd4;
|
|
end
|
|
|
|
end else begin
|
|
|
|
// cant wrap during a burst
|
|
if (snd_sptr > snd_endc) begin
|
|
|
|
snd_sptr <= snd_start;
|
|
|
|
if (snd_next_valid == 1'b1) begin
|
|
snd_endc <= snd_endn;
|
|
snd_next_valid <= 1'b0;
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
wire [21:2] ram_page = memc_control[3:2] == 2'b00 ? {3'd0, cpu_address[18:2]} :
|
|
memc_control[3:2] == 2'b01 ? {2'd0, cpu_address[19:2]} :
|
|
memc_control[3:2] == 2'b10 ? {1'd0, cpu_address[20:2]} :
|
|
cpu_address[21:2];
|
|
|
|
assign mem_addr_o = vid_load ? {5'd0, vid_address[18:2]} :
|
|
cur_load ? {5'd0, cur_address[18:2]} :
|
|
snd_load ? {5'd0, snd_sptr[18:2]} :
|
|
caddr;
|
|
|
|
wire [23:2] caddr = phycs ? {2'd0, ram_page} : // use physical memory
|
|
romcs ? {3'b010, cpu_address[20:2]} : // use 2mb and up for rom space.
|
|
table_valid & logcs ? phys_address[23:2] : // use logical memory.
|
|
22'd0;
|
|
|
|
assign mem_cyc_o = cpu_load ? cpu_cyc & ~err : dma_in_progress;
|
|
assign mem_stb_o = cpu_load ? cpu_stb : dma_in_progress;
|
|
assign mem_sel_o = cpu_load ? cpu_sel : 4'b1111;
|
|
assign mem_we_o = cpu_load ? cpu_mem_we : 1'b0;
|
|
assign mem_cti_o = 3'b010;
|
|
|
|
wire address_valid = (logcs & table_valid) | rom_low_cs| ioc_cs | memw | tablew | vidc_cs | (phycs & ~cpu_we) | (phycs & spvmd & cpu_we) | romcs;
|
|
wire err = ~address_valid;
|
|
|
|
assign cpu_ack = (mem_we_o ? mem_ack_i : cache_ack) & ~err;
|
|
assign cpu_err = cpu_load ? mem_ack_i & err : 1'b0;
|
|
assign cpu_dout = cache_data[caddr[3:2]];
|
|
wire cpu_mem_we = cpu_we & ((phycs & spvmd) | (table_valid & logcs)) & ~romcs;
|
|
|
|
assign tablew = cpu_cyc & cpu_we & spvmd & (cpu_address[25:23] == 3'b111) & (cpu_address[12] == 0) & (cpu_address[7] == 0); // &3800000+
|
|
wire memw = cpu_cyc & cpu_we & spvmd & (cpu_address[25:21] == 5'b11011); // &3600000
|
|
assign vidw = cpu_cyc & cpu_we & vidc_cs; // &3400000
|
|
|
|
// bus chip selects
|
|
wire logcs = cpu_address[25] == 1'b0; // 0000000 - 1FFFFFF
|
|
assign phycs = cpu_address[25:24] == 2'b10; // 2000000 - 2FFFFFF
|
|
assign ioc_cs = spvmd & (cpu_address[25:22] == 4'b1100); // 3000000 - 33FFFFF
|
|
wire vidc_cs = spvmd & (cpu_address[25:21] == 5'b11010); // 3400000 - 35FFFFF (WE & SPVMD)
|
|
assign rom_low_cs = (cpu_address[25:22] == 4'b1101); // 3400000 - 37FFFFF
|
|
assign romcs = ((cpu_address[25:23] == 3'b111) | (cpu_address[25:19] == 7'h00) & rom_overlay); // 3800000 - 3FFFFFF
|
|
|
|
assign vidak = cpu_load ? 1'b0 : video_dma_ip & mem_ack_i;
|
|
assign sndak = cpu_load ? 1'b0 : sound_dma_ip & mem_ack_i;
|
|
|
|
assign sirq_n = snd_next_valid;
|
|
assign ram_cs = table_valid | phycs | romcs;
|
|
|
|
endmodule
|