mirror of
https://github.com/UzixLS/TSConf_MiST.git
synced 2025-07-18 23:01:37 +03:00
272 lines
9.9 KiB
Verilog
272 lines
9.9 KiB
Verilog
`include "tune.v"
|
|
|
|
// PentEvo project (c) NedoPC 2008-2011
|
|
//
|
|
// DRAM arbiter. Shares DRAM between CPU, video data fetcher and other devices
|
|
//
|
|
|
|
// Arbitration is made on full 8-cycle access blocks. Each cycle is defined by dram.v and consists of 4 fpga clocks.
|
|
// During each access block, there can be either no videodata access, 1 videodata access, 2, 4 or 8 accesses.
|
|
// All spare cycles can be used by CPU or other devices. If no device uses memory in the given cycle, refresh cycle is performed.
|
|
//
|
|
// In each access block, videodata accesses are spreaded all over the block so that CPU receives cycle
|
|
// as fast as possible, until there is absolute need to fetch remaining video data.
|
|
//
|
|
// Examples:
|
|
//
|
|
// | access block | 4 video accesses during block, no processor accesses. video accesses are done
|
|
// | vid | vid | vid | vid | ref | ref | ref | ref | as soon as possible, spare cycles are refresh ones
|
|
//
|
|
// | access block | 4 video accesses during block, processor requests access every other cycle
|
|
// | vid | prc | vid | prc | vid | prc | vid | prc |
|
|
//
|
|
// | access block | 4 video accesses, processor begins requesting cycles continously from second one
|
|
// | vid | prc | prc | prc | prc | vid | vid | vid | so it is given cycles while there is such possibility. after that processor
|
|
// can't access mem until the end of access block and stalls
|
|
//
|
|
// | access block | 8 video accesses, processor stalls, if it is requesting cycles
|
|
// | vid | vid | vid | vid | vid | vid | vid | vid |
|
|
//
|
|
// | access block | 2 video accesses, single processor request, other cycles are refresh ones
|
|
// | vid | vid | ref | ref | cpu | ref | ref | ref |
|
|
//
|
|
// | access block | 4 video accesses, single processor request, other cycles are refresh ones
|
|
// | vid | vid | cpu | vid | vid | ref | ref | ref |
|
|
//
|
|
// access block begins at any dram cycle, then blocks video_go back-to-back
|
|
//
|
|
// key signals are video_go and XXX_req, sampled at the end of each dram cycle. Must be set to the module at c3 clock cycle.
|
|
|
|
// CPU can have either normal or lower access priority to the DRAM.
|
|
// At the INT active (32 of 3.5MHz clocks) the priority is raised to normal, so that CPU won't miss its interrupt.
|
|
// This should be considered if dummy RAM access used for waiting for the end of DMA operation instead of status bit polling.
|
|
//
|
|
// DRAM access priority:
|
|
// Z80 normal Z80 low
|
|
// - VIDEO - VIDEO
|
|
// - CPU - TS
|
|
// - TM - TM
|
|
// - TS - DMA
|
|
// - DMA - CPU
|
|
|
|
module arbiter
|
|
(
|
|
input wire clk,
|
|
input wire c1,
|
|
input wire c2,
|
|
input wire c3,
|
|
input wire cyc,
|
|
|
|
// dram.v interface
|
|
output wire [22:0] dram_addr, // address for dram access
|
|
output wire dram_req, // dram request
|
|
output wire dram_rnw, // Read-NotWrite
|
|
output wire [ 1:0] dram_bsel, // byte select: bsel[1] for wrdata[15:8], bsel[0] for wrdata[7:0]
|
|
output wire [15:0] dram_wrdata, // data to be written
|
|
|
|
// video
|
|
input wire [20:0] video_addr, // during access block, only when video_strobe==1
|
|
input wire video_go, // start video access blocks
|
|
input wire [ 4:0] video_bw, // [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
|
|
// [2:0] - need cycles
|
|
output wire video_pre_next, // (c1)
|
|
output wire video_next, // (c2) at this signal video_addr may be changed; it is one clock leading the video_strobe
|
|
output wire video_strobe, // (c3) one-cycle strobe meaning that video_data is available
|
|
output wire next_vid, // used for TM prefetch
|
|
|
|
// CPU
|
|
input wire [20:0] cpu_addr,
|
|
input wire [ 7:0] cpu_wrdata,
|
|
input wire cpu_req,
|
|
input wire cpu_rnw,
|
|
input wire cpu_wrbsel,
|
|
input wire cpu_csrom,
|
|
output reg cpu_next, // next cycle is allowed to be used by CPU
|
|
output reg cpu_strobe,
|
|
output reg cpu_latch,
|
|
output wire curr_cpu_o,
|
|
|
|
// DMA
|
|
input wire [20:0] dma_addr,
|
|
input wire [15:0] dma_wrdata,
|
|
input wire dma_req,
|
|
input wire dma_rnw,
|
|
output wire dma_next,
|
|
|
|
// TS
|
|
input wire [20:0] ts_addr,
|
|
input wire ts_req,
|
|
output wire ts_pre_next,
|
|
output wire ts_next,
|
|
|
|
// TM
|
|
input wire [20:0] tm_addr,
|
|
input wire tm_req,
|
|
output wire tm_next,
|
|
|
|
// ROM loader
|
|
input wire loader_clk,
|
|
input wire [15:0] loader_addr,
|
|
input wire [7:0] loader_data,
|
|
input wire loader_wr,
|
|
input wire loader_cs_rom_main,
|
|
input wire loader_cs_rom_gs
|
|
);
|
|
|
|
localparam CYCLES = 6;
|
|
|
|
localparam CYC_CPU = 6'b000001;
|
|
localparam CYC_VID = 6'b000010;
|
|
localparam CYC_TS = 6'b000100;
|
|
localparam CYC_TM = 6'b001000;
|
|
localparam CYC_DMA = 6'b010000;
|
|
localparam CYC_LOADER = 6'b100000;
|
|
localparam CYC_FREE = 6'b000000;
|
|
|
|
localparam CPU = 0;
|
|
localparam VIDEO = 1;
|
|
localparam TS = 2;
|
|
localparam TM = 3;
|
|
localparam DMA = 4;
|
|
localparam LOADER = 5;
|
|
|
|
reg [CYCLES-1:0] curr_cycle; // type of the cycle in progress
|
|
reg [CYCLES-1:0] next_cycle; // type of the next cycle
|
|
|
|
reg [2:0] blk_rem = 0; // remaining accesses in a block (7..0)
|
|
reg [2:0] vid_rem = 0; // remaining video accesses in block
|
|
reg stall = 0;
|
|
|
|
wire dev_over_cpu = 0; // can be used to rise devices priority over CPU
|
|
|
|
wire next_cpu = next_cycle[CPU];
|
|
assign next_vid = next_cycle[VIDEO];
|
|
wire next_ts = next_cycle[TS];
|
|
wire next_tm = next_cycle[TM];
|
|
wire next_dma = next_cycle[DMA];
|
|
wire next_loader = next_cycle[LOADER];
|
|
|
|
wire curr_cpu = curr_cycle[CPU];
|
|
wire curr_vid = curr_cycle[VIDEO];
|
|
wire curr_ts = curr_cycle[TS];
|
|
wire curr_tm = curr_cycle[TM];
|
|
wire curr_dma = curr_cycle[DMA];
|
|
wire curr_loader = curr_cycle[LOADER];
|
|
|
|
assign curr_cpu_o = curr_cpu;
|
|
|
|
|
|
// track blk_rem counter:
|
|
// how many cycles left to the end of block (7..0)
|
|
wire video_start = ~|blk_rem;
|
|
wire [2:0] blk_nrem = (video_start && video_go) ? {video_bw[4:3], 1'b1} : (video_start ? 3'd0 : (blk_rem - 3'd1));
|
|
wire bw_full = ~|{video_bw[4] & video_bw[2], video_bw[3] & video_bw[1], video_bw[0]}; // stall when 000/00/0
|
|
wire video_only = stall || (vid_rem == blk_rem);
|
|
wire video_idle = ~|vid_rem;
|
|
|
|
always @(posedge clk) if (c3)
|
|
begin
|
|
blk_rem <= blk_nrem;
|
|
|
|
if (video_start)
|
|
stall <= bw_full & video_go;
|
|
end
|
|
|
|
// track vid_rem counter
|
|
// how many video cycles left to the end of block (7..0)
|
|
wire [2:0] vidmax = {video_bw[2:0]}; // number of cycles for video access
|
|
wire [2:0] vid_nrem_next = video_idle ? 3'd0 : (vid_rem - 3'd1);
|
|
wire [2:0] vid_nrem_start = (cpu_req && !dev_over_cpu) ? vidmax : (vidmax - 3'd1);
|
|
wire [2:0] vid_nrem = (video_go && video_start) ? vid_nrem_start : (next_vid ? vid_nrem_next : vid_rem);
|
|
|
|
always @(posedge clk) if (c3)
|
|
vid_rem <= vid_nrem;
|
|
|
|
reg loader_wr0;
|
|
reg [7:0] loader_data0;
|
|
reg [1:0] loader_hiaddr;
|
|
always @(posedge loader_clk) begin
|
|
if (loader_wr && (loader_cs_rom_main || loader_cs_rom_gs)) begin
|
|
loader_wr0 <= 1'd1;
|
|
loader_data0 <= loader_data;
|
|
loader_hiaddr <= { loader_cs_rom_gs, loader_cs_rom_main };
|
|
end
|
|
else if (cyc) begin
|
|
loader_wr0 <= 1'd0;
|
|
end
|
|
end
|
|
|
|
// next cycle decision
|
|
wire [CYCLES-1:0] cyc_dev = tm_req ? CYC_TM : (ts_req ? CYC_TS : CYC_DMA);
|
|
wire dev_req = ts_req || tm_req || dma_req;
|
|
|
|
always @*
|
|
if (loader_wr0) begin
|
|
cpu_next = 1'b0;
|
|
next_cycle = CYC_LOADER;
|
|
end
|
|
else
|
|
if (video_start) // video burst start
|
|
if (video_go) // video active
|
|
begin
|
|
cpu_next = dev_over_cpu ? 1'b0 : !bw_full;
|
|
next_cycle = dev_over_cpu ? CYC_VID : (bw_full ? CYC_VID : (cpu_req ? CYC_CPU : CYC_VID));
|
|
end
|
|
|
|
else // video idle
|
|
begin
|
|
cpu_next = !dev_over_cpu;
|
|
next_cycle = dev_over_cpu ? cyc_dev : (cpu_req ? CYC_CPU : (dev_req ? cyc_dev : CYC_FREE));
|
|
end
|
|
|
|
else // video burst in progress
|
|
begin
|
|
cpu_next = dev_over_cpu ? 1'b0 : !video_only;
|
|
next_cycle = video_only ? CYC_VID : (dev_over_cpu ? cyc_dev : (cpu_req ? CYC_CPU : (!video_idle ? CYC_VID : (dev_req ? cyc_dev : CYC_FREE))));
|
|
end
|
|
|
|
always @(posedge clk) if (c3)
|
|
curr_cycle <= next_cycle;
|
|
|
|
|
|
// DRAM interface
|
|
assign dram_wrdata = curr_loader? {2{loader_data0}} : curr_dma ? dma_wrdata : {2{cpu_wrdata[7:0]}}; // write data has to be clocked at c0 in dram.v
|
|
assign dram_bsel[1:0] = next_loader? {loader_addr[0], ~loader_addr[0]} : next_dma ? 2'b11 : {cpu_wrbsel, ~cpu_wrbsel};
|
|
assign dram_req = |next_cycle;
|
|
assign dram_rnw = next_loader? 1'b0 : next_cpu ? cpu_rnw : (next_dma ? dma_rnw : 1'b1);
|
|
assign dram_addr = {23{next_loader}} & { loader_hiaddr, 6'b000000, loader_addr[15:1] }
|
|
| {23{next_cpu}} & { 1'b0, cpu_csrom, {6{~cpu_csrom}} & cpu_addr[20:15], cpu_addr[14:0] }
|
|
| {23{next_vid}} & { 2'b0, video_addr }
|
|
| {23{next_ts}} & { 2'b0, ts_addr }
|
|
| {23{next_tm}} & { 2'b0, tm_addr }
|
|
| {23{next_dma}} & { 2'b0, dma_addr };
|
|
|
|
reg cpu_rnw_r;
|
|
always @(posedge clk) if (c3)
|
|
cpu_rnw_r <= cpu_rnw;
|
|
|
|
// read strobes generation for video and cpu
|
|
always @(posedge clk)
|
|
if (c1)
|
|
begin
|
|
cpu_strobe <= curr_cpu && cpu_rnw_r;
|
|
cpu_latch <= curr_cpu && cpu_rnw_r;
|
|
end
|
|
else if (c2)
|
|
cpu_strobe <= 1'b0;
|
|
else if (c3)
|
|
cpu_latch <= 1'b0;
|
|
|
|
assign video_pre_next = curr_vid & c1;
|
|
assign video_next = curr_vid & c2;
|
|
assign video_strobe = curr_vid && c3;
|
|
|
|
assign ts_pre_next = curr_ts & c1;
|
|
assign ts_next = curr_ts & c2;
|
|
|
|
assign tm_next = curr_tm & c2;
|
|
|
|
assign dma_next = curr_dma & c2;
|
|
|
|
endmodule
|