update tsconf to commit 83afbba6f5d366f96297028aa3d64512fa254a51

This commit is contained in:
Eugene Lozovoy
2024-09-12 16:28:38 +03:00
parent ba7d903e12
commit 3681138b01
47 changed files with 5648 additions and 4341 deletions

View File

@ -285,3 +285,6 @@ set_global_assignment -name PRE_FLOW_SCRIPT_FILE "quartus_sh:build_id_verilog.tc
set_global_assignment -name QIP_FILE pll.qip set_global_assignment -name QIP_FILE pll.qip
set_global_assignment -name QIP_FILE files.qip set_global_assignment -name QIP_FILE files.qip
set_global_assignment -name QIP_FILE "mist-modules/mist.qip" set_global_assignment -name QIP_FILE "mist-modules/mist.qip"
set_global_assignment -name SYSTEMVERILOG_FILE TSConf.sv
set_global_assignment -name SDC_FILE TSConf.sdc

View File

@ -35,13 +35,13 @@ set_false_path -to [get_ports {AUDIO_R}]
set_false_path -to [get_ports {LED}] set_false_path -to [get_ports {LED}]
set_false_path -from [get_ports {UART_RX}] set_false_path -from [get_ports {UART_RX}]
set_multicycle_path -to {tsconf|U16|*} -setup 2
set_multicycle_path -to {tsconf|U16|*} -hold 1
set_multicycle_path -from {tsconf|CPU|*} -setup 2 set_multicycle_path -from {tsconf|CPU|*} -setup 2
set_multicycle_path -from {tsconf|CPU|*} -hold 1 set_multicycle_path -from {tsconf|CPU|*} -hold 1
set_multicycle_path -to {tsconf|CPU|*} -setup 2 set_multicycle_path -to {tsconf|CPU|*} -setup 2
set_multicycle_path -to {tsconf|CPU|*} -hold 1 set_multicycle_path -to {tsconf|CPU|*} -hold 1
set_multicycle_path -to {tsconf|U15|*} -setup 2 set_multicycle_path -to {tsconf|saa1099|*} -setup 2
set_multicycle_path -to {tsconf|U15|*} -hold 1 set_multicycle_path -to {tsconf|saa1099|*} -hold 1
set_multicycle_path -to {tsconf|gs|*} -setup 2
set_multicycle_path -to {tsconf|gs|*} -hold 1

View File

@ -13,6 +13,22 @@
{ "" "" "" "Port \"reset_value\" on the entity instantiation of \"h_counter\" is connected to a signal of width 32. The formal width of the signal in the module is 16. The extra bits will be ignored." { } { } 0 12020 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "Port \"reset_value\" on the entity instantiation of \"h_counter\" is connected to a signal of width 32. The formal width of the signal in the module is 16. The extra bits will be ignored." { } { } 0 12020 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Overwriting existing clock: vip\|hps\|fpga_interfaces\|clocks_resets\|h2f_user0_clk" { } { } 0 332043 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "Overwriting existing clock: vip\|hps\|fpga_interfaces\|clocks_resets\|h2f_user0_clk" { } { } 0 332043 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "LOCKED port on the PLL is not properly connected on instance \"emu:emu\|pll:pll\|pll_0002:pll_inst\|altera_pll:altera_pll_i\|general\[0\].gpll\". The LOCKED port on the PLL should be connected when the FBOUTCLK port is connected. Although it is unnecessary to connect the LOCKED signal, any logic driven off of an output clock of the PLL will not know when the PLL is locked and ready." { } { } 0 21300 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "LOCKED port on the PLL is not properly connected on instance \"emu:emu\|pll:pll\|pll_0002:pll_inst\|altera_pll:altera_pll_i\|general\[0\].gpll\". The LOCKED port on the PLL should be connected when the FBOUTCLK port is connected. Although it is unnecessary to connect the LOCKED signal, any logic driven off of an output clock of the PLL will not know when the PLL is locked and ready." { } { } 0 21300 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lut.data_a\" at jt49_exp.v(37) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lut.waddr_a\" at jt49_exp.v(37) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lut.we_a\" at jt49_exp.v(37) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lfo_sh1_lut.data_a\" at jt12_pm.v(38) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lfo_sh1_lut.waddr_a\" at jt12_pm.v(38) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lfo_sh2_lut.data_a\" at jt12_pm.v(39) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lfo_sh2_lut.waddr_a\" at jt12_pm.v(39) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lfo_sh1_lut.we_a\" at jt12_pm.v(38) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Net \"lfo_sh2_lut.we_a\" at jt12_pm.v(39) has no driver or initial value, using a default initial value '0'" { } { } 0 10030 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL or VHDL warning at video_out.v(83): object \"red0\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL or VHDL warning at video_out.v(84): object \"grn0\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL or VHDL warning at video_out.v(85): object \"blu0\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL or VHDL warning at video_out.v(86): object \"red1\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL or VHDL warning at video_out.v(87): object \"grn1\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL or VHDL warning at video_out.v(88): object \"blu1\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Verilog HDL assignment warning at zports.v(572): truncated value with size 8 to match size of target (2)" { } { } 0 10230 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "*" { } { } 0 292013 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "*" { } { } 0 292013 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "Vip.vip: Module dependency loop involving: \"HPS\"" { } { } 0 9999 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "Vip.vip: Module dependency loop involving: \"HPS\"" { } { } 0 9999 "" 0 0 "Quartus II" 0 -1 0 ""}
{ "" "" "" "alt_vip_common_frame_counter.v" { } { } 0 9999 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "alt_vip_common_frame_counter.v" { } { } 0 9999 "" 0 0 "Quartus II" 0 -1 0 ""}

View File

@ -301,9 +301,7 @@ sd_card sd_card
//////////////////// MAIN ////////////////////// //////////////////// MAIN //////////////////////
wire [7:0] R,G,B; wire [7:0] R,G,B;
wire HBlank,VBlank;
wire VS, HS; wire VS, HS;
wire ce_vid;
wire [15:0] SOUND_L; wire [15:0] SOUND_L;
wire [15:0] SOUND_R; wire [15:0] SOUND_R;
@ -324,14 +322,11 @@ tsconf tsconf
.SDRAM_nCS(SDRAM_nCS), .SDRAM_nCS(SDRAM_nCS),
// .SDRAM_CLK(SDRAM_CLK), // .SDRAM_CLK(SDRAM_CLK),
.VGA_R(R), .VRED(R),
.VGA_G(G), .VGRN(G),
.VGA_B(B), .VBLU(B),
.VGA_HS(HS), .VHSYNC(HS),
.VGA_VS(VS), .VVSYNC(VS),
.VGA_HBLANK(HBlank),
.VGA_VBLANK(VBlank),
.VGA_CEPIX(ce_vid),
.SD_SO(sdmiso), .SD_SO(sdmiso),
.SD_SI(sdmosi), .SD_SI(sdmosi),
@ -345,12 +340,13 @@ tsconf tsconf
.WARM_RESET(buttons[1]), .WARM_RESET(buttons[1]),
.RTC(rtc), .RTC(rtc),
.OUT0(~status[30]), .OUT0(~status[30]),
.TAPE_IN(UART_RX),
.CMOSCfg(CMOSCfg), .CMOSCfg(CMOSCfg),
.PS2_KEY({key_strobe,key_pressed,key_extended,key_code}), .PS2_KEY({key_strobe,key_pressed,key_extended,key_code}),
.PS2_MOUSE(ps2_mouse), .PS2_MOUSE(ps2_mouse),
.joystick(joystick_0[5:0] | joystick_1[5:0]), .JOYSTICK(joystick_0 | joystick_1),
.loader_act(ioctl_download), .loader_act(ioctl_download),
.loader_addr(ioctl_addr[15:0]), .loader_addr(ioctl_addr[15:0]),

View File

@ -1,20 +1,20 @@
set_global_assignment -name QIP_FILE rtl/T80/T80.qip set_global_assignment -name QIP_FILE rtl/T80/T80.qip
set_global_assignment -name VERILOG_FILE rtl/memory/dma.v set_global_assignment -name VERILOG_FILE rtl/common/clock.v
set_global_assignment -name VERILOG_FILE rtl/memory/arbiter.v set_global_assignment -name VERILOG_FILE rtl/common/dma.v
set_global_assignment -name VERILOG_FILE rtl/memory/sdram.v set_global_assignment -name VERILOG_FILE rtl/common/resetter.v
set_global_assignment -name VERILOG_FILE rtl/memory/dpram.v set_global_assignment -name VERILOG_FILE rtl/common/spi.v
set_global_assignment -name VERILOG_FILE rtl/common/zsignals.v set_global_assignment -name VERILOG_FILE rtl/dram/arbiter.v
set_global_assignment -name VERILOG_FILE rtl/common/zports.v set_global_assignment -name VERILOG_FILE rtl/dram/dpram.v
set_global_assignment -name VERILOG_FILE rtl/common/zmem.v set_global_assignment -name VERILOG_FILE rtl/dram/sdram.v
set_global_assignment -name VERILOG_FILE rtl/common/zmaps.v set_global_assignment -name VERILOG_FILE rtl/periph/kempston_mouse.v
set_global_assignment -name VERILOG_FILE rtl/common/zint.v set_global_assignment -name VHDL_FILE rtl/periph/keyboard.vhd
set_global_assignment -name VERILOG_FILE rtl/common/zclock.v set_global_assignment -name VERILOG_FILE rtl/periph/mc146818a.v
set_global_assignment -name VERILOG_FILE rtl/rtc/mc146818a.v set_global_assignment -name VERILOG_FILE rtl/periph/vdac.v
set_global_assignment -name VHDL_FILE rtl/sound/soundrive.vhd set_global_assignment -name VHDL_FILE rtl/sound/soundrive.vhd
set_global_assignment -name QIP_FILE rtl/sound/jt12/jt03.qip set_global_assignment -name QIP_FILE rtl/sound/jt12/jt03.qip
set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/turbosound.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/turbosound.sv
set_global_assignment -name VERILOG_FILE rtl/sound/gs.v
set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/saa1099.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/saa1099.sv
set_global_assignment -name VERILOG_FILE rtl/sound/gs.v
set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/compressor.sv set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/compressor.sv
set_global_assignment -name VERILOG_FILE rtl/video/video_ts_render.v set_global_assignment -name VERILOG_FILE rtl/video/video_ts_render.v
set_global_assignment -name VERILOG_FILE rtl/video/video_ts.v set_global_assignment -name VERILOG_FILE rtl/video/video_ts.v
@ -25,10 +25,11 @@ set_global_assignment -name VERILOG_FILE rtl/video/video_out.v
set_global_assignment -name VERILOG_FILE rtl/video/video_mode.v set_global_assignment -name VERILOG_FILE rtl/video/video_mode.v
set_global_assignment -name VERILOG_FILE rtl/video/video_fetch.v set_global_assignment -name VERILOG_FILE rtl/video/video_fetch.v
set_global_assignment -name VERILOG_FILE rtl/video/video_top.v set_global_assignment -name VERILOG_FILE rtl/video/video_top.v
set_global_assignment -name VHDL_FILE rtl/keyboard.vhd set_global_assignment -name VERILOG_FILE rtl/z80/zclock.v
set_global_assignment -name VERILOG_FILE rtl/kempston_mouse.v set_global_assignment -name VERILOG_FILE rtl/z80/zint.v
set_global_assignment -name VERILOG_FILE rtl/spi.v set_global_assignment -name VERILOG_FILE rtl/z80/zmaps.v
set_global_assignment -name VERILOG_FILE rtl/clock.v set_global_assignment -name VERILOG_FILE rtl/z80/zmem.v
set_global_assignment -name VERILOG_FILE rtl/z80/zports.v
set_global_assignment -name VERILOG_FILE rtl/z80/zsignals.v
set_global_assignment -name VERILOG_FILE rtl/tsconf.v set_global_assignment -name VERILOG_FILE rtl/tsconf.v
set_global_assignment -name SDC_FILE TSConf.sdc set_global_assignment -name SEARCH_PATH rtl/
set_global_assignment -name SYSTEMVERILOG_FILE TSConf.sv

View File

@ -1,33 +0,0 @@
// This module receives 28 MHz as input clock
// and strobes strobes for all clocked parts
// clk|-__--__--__--__-| period = 28 duty = 50% phase = 0
// cnt|< 0>< 1>< 2>< 3>|
// f0 |----____----____| period = 14 duty = 50% phase = 0
// f1 |____----____----| period = 14 duty = 50% phase = 180
// h0 |--------________| period = 7 duty = 50% phase = 0
// h1 |________--------| period = 7 duty = 50% phase = 180
// c0 |----____________| period = 7 duty = 25% phase = 0
// c1 |____----________| period = 7 duty = 25% phase = 90
// c2 |________----____| period = 7 duty = 25% phase = 180
// c3 |____________----| period = 7 duty = 25% phase = 270
module clock
(
input wire clk,
output reg f0, f1,
output reg h0, h1,
output reg c0, c1, c2, c3
);
reg [1:0] cnt;
always @(posedge clk) begin
cnt <= cnt + 2'b1;
{f1, f0} <= 2'b1 << cnt[0];
{h1, h0} <= 2'b1 << cnt[1];
{c3, c2, c1, c0} <= 4'b1 << cnt;
end
endmodule

61
rtl/common/clock.v Normal file
View File

@ -0,0 +1,61 @@
// This module receives 28 MHz as input clock
// and makes strobes for all clocked parts
// clk |<EFBFBD>__<EFBFBD><EFBFBD>__<EFBFBD><EFBFBD>__<EFBFBD><EFBFBD>__<EFBFBD>| period = 28 duty = 50% phase = 0
// cnt |< 0>< 1>< 2>< 3>|
// f0 |<EFBFBD><EFBFBD><EFBFBD><EFBFBD>____<EFBFBD><EFBFBD><EFBFBD><EFBFBD>____| period = 14 duty = 50% phase = 0
// f1 |____<EFBFBD><EFBFBD><EFBFBD><EFBFBD>____<EFBFBD><EFBFBD><EFBFBD><EFBFBD>| period = 14 duty = 50% phase = 180
// h0 |<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>________| period = 7 duty = 50% phase = 0
// h1 |________<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>| period = 7 duty = 50% phase = 180
// c0 |<EFBFBD><EFBFBD><EFBFBD><EFBFBD>____________| period = 7 duty = 25% phase = 0
// c1 |____<EFBFBD><EFBFBD><EFBFBD><EFBFBD>________| period = 7 duty = 25% phase = 90
// c2 |________<EFBFBD><EFBFBD><EFBFBD><EFBFBD>____| period = 7 duty = 25% phase = 180
// c3 |____________<EFBFBD><EFBFBD><EFBFBD><EFBFBD>| period = 7 duty = 25% phase = 270
`include "tune.v"
module clock
(
input wire clk,
input wire [1:0] ay_mod,
output wire f0, f1,
output wire h0, h1,
output wire c0, c1, c2, c3,
output wire ay_clk
);
reg [1:0] f = 'b01;
reg [1:0] h = 'b01;
reg [3:0] c = 'b0001;
always @(posedge clk)
begin
f <= ~f;
if (f[1]) h <= ~h;
c <= {c[2:0], c[3]};
end
assign f0 = f[0];
assign f1 = f[1];
assign h0 = h[0];
assign h1 = h[1];
assign c0 = c[0];
assign c1 = c[1];
assign c2 = c[2];
assign c3 = c[3];
// AY clock generator
// ay_mod - clock selection for AY, MHz: 00 - 1.75 / 01 - 1.7733 / 10 - 3.5 / 11 - 3.546
reg [7:0] skip_cnt = 0;
reg [3:0] ay_cnt = 0;
assign ay_clk = ay_mod[1] ? ay_cnt[2] : ay_cnt[3];
always @(posedge clk)
begin
skip_cnt <= skip_cnt[7] ? 8'd73 : skip_cnt - 8'd1;
ay_cnt <= ay_cnt + (skip_cnt[7] & ay_mod[0] ? 4'd2 : 4'd1);
end
endmodule

View File

@ -1,59 +1,74 @@
// This module serves direct DRAM-to-device data transfer // This module serves direct DRAM-to-device data transfer
// to do `include "tune.v"
// - probably add the extra 8 bit counter for number of bursts
module dma module dma
( (
// clocks // clocks
input wire clk, input wire clk,
input wire c2, input wire c2,
input wire reset, input wire rst_n,
// interface // interface
input wire [8:0] dmaport_wr, `ifdef FDR
output wire dma_act, input wire [9:0] dmaport_wr,
output reg [15:0] data, `else
output wire [ 7:0] wraddr, input wire [8:0] dmaport_wr,
output wire int_start, `endif
output wire dma_act,
output reg [15:0] data = 0,
output wire [ 7:0] wraddr,
output wire int_start,
// Z80 // Z80
input wire [7:0] zdata, input wire [7:0] zdata,
// DRAM interface // DRAM interface
output wire [20:0] dram_addr, output wire [20:0] dram_addr,
input wire [15:0] dram_rddata, input wire [15:0] dram_rddata,
output wire [15:0] dram_wrdata, output wire [15:0] dram_wrdata,
output wire dram_req, output wire dram_req,
output wire dram_rnw, output wire dram_rnw,
input wire dram_next, input wire dram_next,
// SPI interface // SPI interface
input wire [7:0] spi_rddata, input wire [7:0] spi_rddata,
output wire [7:0] spi_wrdata, output wire [7:0] spi_wrdata,
output wire spi_req, output wire spi_req,
input wire spi_stb, input wire spi_stb,
// IDE interface // WTPORT interface
input wire [15:0] ide_in, input wire [7:0] wtp_rddata,
output wire [15:0] ide_out, // output wire [7:0] wtp_wrdata,
output wire ide_req, output wire wtp_req,
output wire ide_rnw, input wire wtp_stb,
input wire ide_stb,
// CRAM interface // IDE interface
output wire cram_we, input wire [15:0] ide_in,
output wire [15:0] ide_out,
output wire ide_req,
output wire ide_rnw,
input wire ide_stb,
// SFILE interface `ifdef FDR
output wire sfile_we // FDD interface
input wire [7:0] fdr_in,
output wire fdr_req,
input wire fdr_stb,
input wire fdr_stop,
`endif
// CRAM interface
output wire cram_we,
// SFILE interface
output wire sfile_we
); );
// mode: // mode:
// 0 - device to RAM (read from device) // 0 - device to RAM (read from device)
// 1 - RAM to device (write to device) // 1 - RAM to device (write to device)
assign wraddr = d_addr[7:0];
wire dma_saddrl = dmaport_wr[0]; wire dma_saddrl = dmaport_wr[0];
wire dma_saddrh = dmaport_wr[1]; wire dma_saddrh = dmaport_wr[1];
wire dma_saddrx = dmaport_wr[2]; wire dma_saddrx = dmaport_wr[2];
@ -63,72 +78,84 @@ module dma
wire dma_len = dmaport_wr[6]; wire dma_len = dmaport_wr[6];
wire dma_launch = dmaport_wr[7]; wire dma_launch = dmaport_wr[7];
wire dma_num = dmaport_wr[8]; wire dma_num = dmaport_wr[8];
`ifdef FDR
wire dma_numh = dmaport_wr[9];
`endif
// DRAM // devices
assign dram_addr = state_rd ? ((!dv_blt || !phase_blt) ? s_addr : d_addr) : d_addr; localparam DEV_RAM = 3'b001;
assign dram_wrdata = data;
assign dram_req = dma_act && state_mem;
assign dram_rnw = state_rd;
// devices
localparam DEV_RAM = 3'b0001;
localparam DEV_BLT1 = 4'b1001; localparam DEV_BLT1 = 4'b1001;
`ifdef XTR_FEAT
localparam DEV_BLT2 = 4'b0110; localparam DEV_BLT2 = 4'b0110;
`endif
localparam DEV_FIL = 4'b0100; localparam DEV_FIL = 4'b0100;
localparam DEV_SPI = 3'b010; localparam DEV_SPI = 3'b010;
localparam DEV_IDE = 3'b011; localparam DEV_IDE = 3'b011;
localparam DEV_CRM = 4'b1100; localparam DEV_CRM = 4'b1100;
localparam DEV_SFL = 4'b1101; localparam DEV_SFL = 4'b1101;
localparam DEV_FDD = 4'b0101; localparam DEV_FDD = 4'b0101;
localparam DEV_WTP = 4'b0111;
wire state_dev;
wire ide_int_stb;
wire byte_sw_stb;
wire spi_int_stb;
wire wtp_int_stb;
reg dma_salgn;
reg dma_dalgn;
reg dma_asz;
reg phase; // 0 - read / 1 - write
reg phase_blt; // 0 - source / 1 - destination
reg bsel; // 0 - lsb / 1 - msb
reg dma_opt;
reg [3:0] device;
wire [2:0] dev_bid = device[2:0]; // bidirectional wire [2:0] dev_bid = device[2:0]; // bidirectional
wire [3:0] dev_uni = device[3:0]; // unidirectional wire [3:0] dev_uni = device[3:0]; // unidirectional
wire dma_wnr = device[3]; // 0 - device to RAM / 1 - RAM to device wire dma_wnr = device[3]; // 0 - device to RAM / 1 - RAM to device
`ifdef XTR_FEAT
wire dv_ram = (dev_uni == DEV_RAM) || (dev_uni == DEV_BLT1) || (dev_uni == DEV_BLT2) || (dev_uni == DEV_FIL); wire dv_ram = (dev_uni == DEV_RAM) || (dev_uni == DEV_BLT1) || (dev_uni == DEV_BLT2) || (dev_uni == DEV_FIL);
wire dv_blt = (dev_uni == DEV_BLT1) || (dev_uni == DEV_BLT2); wire dv_blt = (dev_uni == DEV_BLT1) || (dev_uni == DEV_BLT2);
`else
wire dv_ram = (dev_uni == DEV_RAM) || (dev_uni == DEV_BLT1) || (dev_uni == DEV_FIL);
wire dv_blt = (dev_uni == DEV_BLT1);
`endif
wire dv_fil = (dev_uni == DEV_FIL); wire dv_fil = (dev_uni == DEV_FIL);
wire dv_spi = (dev_bid == DEV_SPI); wire dv_spi = (dev_bid == DEV_SPI);
wire dv_ide = (dev_bid == DEV_IDE); wire dv_ide = (dev_bid == DEV_IDE);
wire dv_crm = (dev_uni == DEV_CRM); wire dv_crm = (dev_uni == DEV_CRM);
wire dv_sfl = (dev_uni == DEV_SFL); wire dv_sfl = (dev_uni == DEV_SFL);
wire dv_wtp = (dev_uni == DEV_WTP);
`ifdef FDR
wire dv_fdd = (dev_uni == DEV_FDD);
`endif
wire dev_req = dma_act && state_dev; wire dev_req = dma_act && state_dev;
wire dev_stb = cram_we || sfile_we || ide_int_stb || (spi_int_stb && bsel && dma_act); wire dev_stb = cram_we || sfile_we || ide_int_stb || (byte_sw_stb && bsel && dma_act);
assign cram_we = dev_req && dv_crm && state_wr;
assign sfile_we = dev_req && dv_sfl && state_wr;
// SPI `ifdef FDR
wire spi_int_stb = dv_spi && spi_stb; assign byte_sw_stb = spi_int_stb || wtp_int_stb || fdr_int_stb;
assign spi_wrdata = {8{state_rd}} | (bsel ? data[15:8] : data[7:0]); // send FF on read cycles `else
assign spi_req = dev_req && dv_spi; assign byte_sw_stb = spi_int_stb || wtp_int_stb;
`endif
// IDE
wire ide_int_stb = dv_ide && ide_stb;
assign ide_out = data;
assign ide_req = dev_req && dv_ide;
assign ide_rnw = state_rd;
// blitter // blitter
wire [15:0] blt_rddata = (dev_uni == DEV_BLT1) ? blt1_rddata : blt2_rddata;
// Mode 1 // Mode 1
wire [15:0] blt1_rddata = {blt1_data_h, blt1_data_l};
wire [7:0] blt1_data_h = dma_asz ? blt1_data32 : {blt1_data3, blt1_data2};
wire [7:0] blt1_data_l = dma_asz ? blt1_data10 : {blt1_data1, blt1_data0};
wire [7:0] blt1_data32 = |data[15:8] ? data[15:8] : dram_rddata[15:8];
wire [7:0] blt1_data10 = |data[7:0] ? data[7:0] : dram_rddata[7:0]; wire [7:0] blt1_data10 = |data[7:0] ? data[7:0] : dram_rddata[7:0];
wire [3:0] blt1_data3 = |data[15:12] ? data[15:12] : dram_rddata[15:12]; wire [7:0] blt1_data32 = |data[15:8] ? data[15:8] : dram_rddata[15:8];
wire [3:0] blt1_data2 = |data[11:8] ? data[11:8] : dram_rddata[11:8];
wire [3:0] blt1_data1 = |data[7:4] ? data[7:4] : dram_rddata[7:4];
wire [3:0] blt1_data0 = |data[3:0] ? data[3:0] : dram_rddata[3:0]; wire [3:0] blt1_data0 = |data[3:0] ? data[3:0] : dram_rddata[3:0];
wire [3:0] blt1_data1 = |data[7:4] ? data[7:4] : dram_rddata[7:4];
wire [3:0] blt1_data2 = |data[11:8] ? data[11:8] : dram_rddata[11:8];
wire [3:0] blt1_data3 = |data[15:12] ? data[15:12] : dram_rddata[15:12];
wire [7:0] blt1_data_l = dma_asz ? blt1_data10 : {blt1_data1, blt1_data0};
wire [7:0] blt1_data_h = dma_asz ? blt1_data32 : {blt1_data3, blt1_data2};
wire [15:0] blt1_rddata = {blt1_data_h, blt1_data_l};
`ifdef XTR_FEAT
// Mode 2 // Mode 2
wire [15:0] blt2_rddata = {blt2_data_1, blt2_data_0};
wire [7:0] blt2_data_1 = dma_asz ? blt2_8_data1 : {blt2_4_data3, blt2_4_data2};
wire [7:0] blt2_data_0 = dma_asz ? blt2_8_data0 : {blt2_4_data1, blt2_4_data0};
localparam msk = 8'd255; localparam msk = 8'd255;
wire [8:0] sum80 = data[7:0] + dram_rddata[7:0]; wire [8:0] sum80 = data[7:0] + dram_rddata[7:0];
@ -146,38 +173,26 @@ module dma
wire [3:0] blt2_4_data1 = ((sum41 > msk[3:0]) && dma_opt) ? msk[3:0] : sum41[3:0]; wire [3:0] blt2_4_data1 = ((sum41 > msk[3:0]) && dma_opt) ? msk[3:0] : sum41[3:0];
wire [3:0] blt2_4_data2 = ((sum42 > msk[3:0]) && dma_opt) ? msk[3:0] : sum42[3:0]; wire [3:0] blt2_4_data2 = ((sum42 > msk[3:0]) && dma_opt) ? msk[3:0] : sum42[3:0];
wire [3:0] blt2_4_data3 = ((sum43 > msk[3:0]) && dma_opt) ? msk[3:0] : sum43[3:0]; wire [3:0] blt2_4_data3 = ((sum43 > msk[3:0]) && dma_opt) ? msk[3:0] : sum43[3:0];
wire [7:0] blt2_data_0 = dma_asz ? blt2_8_data0 : {blt2_4_data1, blt2_4_data0};
wire [7:0] blt2_data_1 = dma_asz ? blt2_8_data1 : {blt2_4_data3, blt2_4_data2};
wire [15:0] blt2_rddata = {blt2_data_1, blt2_data_0};
wire [15:0] blt_rddata = (dev_uni == DEV_BLT1) ? blt1_rddata : blt2_rddata;
// data aquiring `else // XTR_FEAT
always @(posedge clk) wire [15:0] blt_rddata = blt1_rddata;
if (state_rd) `endif
begin
if (dram_next)
data <= (dv_blt && phase_blt) ? blt_rddata : dram_rddata;
if (ide_int_stb) // states
data <= ide_in;
if (spi_int_stb)
begin
if (bsel)
data[15:8] <= spi_rddata;
else
data[7:0] <= spi_rddata;
end
end
// states
wire state_rd = ~phase; wire state_rd = ~phase;
wire state_wr = phase; wire state_wr = phase;
wire state_dev = !dv_ram && (dma_wnr ^ !phase); assign state_dev = !dv_ram && (dma_wnr ^ !phase);
wire state_mem = dv_ram || (dma_wnr ^ phase); wire state_mem = dv_ram || (dma_wnr ^ phase);
// states processing // states processing
wire phase_end = phase_end_ram || phase_end_dev; wire blt_hook = dv_blt && !phase_blt && !phase;
wire phase_end_ram = state_mem && dram_next && !blt_hook; wire phase_end_ram = state_mem && dram_next && !blt_hook;
wire phase_end_dev = state_dev && dev_stb; wire phase_end_dev = state_dev && dev_stb;
wire blt_hook = dv_blt && !phase_blt && !phase; wire phase_end = phase_end_ram || phase_end_dev;
wire fil_hook = dv_fil && phase; wire fil_hook = dv_fil && phase;
wire phase_blt_end = state_mem && dram_next && !phase; wire phase_blt_end = state_mem && dram_next && !phase;
@ -187,15 +202,6 @@ module dma
// 0 1 0 read dst // 0 1 0 read dst
// 1 1 0 write dst // 1 1 0 write dst
reg [3:0] device;
reg dma_salgn;
reg dma_dalgn;
reg dma_asz;
reg phase; // 0 - read / 1 - write
reg phase_blt; // 0 - source / 1 - destination
reg bsel; // 0 - lsb / 1 - msb
reg dma_opt;
always @(posedge clk) always @(posedge clk)
if (dma_launch) // write to DMACtrl - launch of DMA burst if (dma_launch) // write to DMACtrl - launch of DMA burst
begin begin
@ -215,26 +221,75 @@ module dma
phase <= ~phase; phase <= ~phase;
if (phase_blt_end) if (phase_blt_end)
phase_blt <= ~phase_blt; phase_blt <= ~phase_blt;
if (spi_int_stb) if (byte_sw_stb)
bsel <= ~bsel; bsel <= ~bsel;
end end
// data aquiring
always @(posedge clk)
if (state_rd)
begin
if (dram_next)
data <= (dv_blt && phase_blt) ? blt_rddata : dram_rddata;
// counter processing if (ide_int_stb)
data <= ide_in;
if (spi_int_stb)
begin
if (bsel)
data[15:8] <= spi_rddata;
else
data[7:0] <= spi_rddata;
end
if (wtp_int_stb)
begin
if (bsel)
data[15:8] <= wtp_rddata;
else
data[7:0] <= wtp_rddata;
end
`ifdef FDR
if (fdr_int_stb)
begin
if (bsel)
data[15:8] <= fdr_in;
else
data[7:0] <= fdr_in;
end
`endif
end
// counter processing
reg [7:0] b_len; // length of burst reg [7:0] b_len; // length of burst
reg [7:0] b_ctr; // counter for cycles in burst
wire [8:0] b_ctr_dec = {1'b0, b_ctr[7:0]} - 9'b1;
wire next_burst = b_ctr_dec[8];
wire [7:0] b_ctr_next = next_burst ? b_len : b_ctr_dec[7:0];
`ifdef FDR
reg [9:0] b_num; // number of bursts
reg [10:0] n_ctr; // counter for bursts
wire [10:0] n_ctr_dec = n_ctr - next_burst;
assign dma_act = !n_ctr[10];
`else
reg [7:0] b_num; // number of bursts reg [7:0] b_num; // number of bursts
reg [8:0] n_ctr; // counter for bursts reg [8:0] n_ctr; // counter for bursts
wire [8:0] n_ctr_dec = n_ctr - next_burst; wire [8:0] n_ctr_dec = n_ctr - next_burst;
assign dma_act = !n_ctr[8]; assign dma_act = !n_ctr[8];
reg [7:0] b_ctr; // counter for cycles in burst `endif
wire [7:0] b_ctr_next = next_burst ? b_len : b_ctr_dec[7:0];
wire [8:0] b_ctr_dec = {1'b0, b_ctr[7:0]} - 9'b1;
wire next_burst = b_ctr_dec[8];
always @(posedge clk) always @(posedge clk)
if (reset) `ifdef FDR
if (!rst_n || (dv_fdd && fdr_stop))
n_ctr[10] <= 1'b1;
`else
if (!rst_n)
n_ctr[8] <= 1'b1; n_ctr[8] <= 1'b1;
`endif
else if (dma_launch) else if (dma_launch)
begin begin
@ -248,31 +303,36 @@ module dma
n_ctr <= n_ctr_dec; n_ctr <= n_ctr_dec;
end end
// loading of burst parameters
// loading of burst parameters
always @(posedge clk) always @(posedge clk)
begin begin
if (dma_len) if (dma_len)
b_len <= zdata; b_len <= zdata;
if (dma_num) if (dma_num)
`ifdef FDR
b_num[7:0] <= zdata;
if (dma_numh)
b_num[9:8] <= zdata[1:0];
`else
b_num <= zdata; b_num <= zdata;
`endif
end end
// address processing
// address processing
// source // source
wire [20:0] s_addr_next = {s_addr_next_h[13:1], s_addr_next_m, s_addr_next_l[6:0]};
wire [13:0] s_addr_next_h = s_addr[20:7] + s_addr_add_h;
wire [1:0] s_addr_add_h = dma_salgn ? {next_burst && dma_asz, next_burst && !dma_asz} : {s_addr_inc_l[8], 1'b0};
wire s_addr_next_m = dma_salgn ? (dma_asz ? s_addr_next_l[7] : s_addr_next_h[0]) : s_addr_inc_l[7];
wire [7:0] s_addr_next_l = (dma_salgn && next_burst) ? s_addr_r : s_addr_inc_l[7:0];
wire [8:0] s_addr_inc_l = {1'b0, s_addr[7:0]} + 9'b1;
reg [20:0] s_addr; // current source address reg [20:0] s_addr; // current source address
reg [7:0] s_addr_r; // source lower address reg [7:0] s_addr_r; // source lower address
wire [8:0] s_addr_inc_l = {1'b0, s_addr[7:0]} + 9'b1;
wire [1:0] s_addr_add_h = dma_salgn ? {next_burst && dma_asz, next_burst && !dma_asz} : {s_addr_inc_l[8], 1'b0};
wire [13:0] s_addr_next_h = s_addr[20:7] + s_addr_add_h;
wire [7:0] s_addr_next_l = (dma_salgn && next_burst) ? s_addr_r : s_addr_inc_l[7:0];
wire s_addr_next_m = dma_salgn ? (dma_asz ? s_addr_next_l[7] : s_addr_next_h[0]) : s_addr_inc_l[7];
wire [20:0] s_addr_next = {s_addr_next_h[13:1], s_addr_next_m, s_addr_next_l[6:0]};
always @(posedge clk) always @(posedge clk)
if ((dram_next || dev_stb) && state_rd && (!dv_blt || !phase_blt)) // increment RAM source addr if ((dram_next || dev_stb) && state_rd && (!dv_blt || !phase_blt)) // increment RAM source addr
s_addr <= s_addr_next; s_addr <= s_addr_next;
@ -296,16 +356,16 @@ module dma
end end
// destination // destination
wire [20:0] d_addr_next = {d_addr_next_h[13:1], d_addr_next_m, d_addr_next_l[6:0]};
wire [13:0] d_addr_next_h = d_addr[20:7] + d_addr_add_h;
wire [1:0] d_addr_add_h = dma_dalgn ? {next_burst && dma_asz, next_burst && !dma_asz} : {d_addr_inc_l[8], 1'b0};
wire d_addr_next_m = dma_dalgn ? (dma_asz ? d_addr_next_l[7] : d_addr_next_h[0]) : d_addr_inc_l[7];
wire [7:0] d_addr_next_l = (dma_dalgn && next_burst) ? d_addr_r : d_addr_inc_l[7:0];
wire [8:0] d_addr_inc_l = {1'b0, d_addr[7:0]} + 9'b1;
reg [20:0] d_addr; // current dest address reg [20:0] d_addr; // current dest address
reg [7:0] d_addr_r; // dest lower address reg [7:0] d_addr_r; // dest lower address
wire [8:0] d_addr_inc_l = {1'b0, d_addr[7:0]} + 9'b1;
wire [1:0] d_addr_add_h = dma_dalgn ? {next_burst && dma_asz, next_burst && !dma_asz} : {d_addr_inc_l[8], 1'b0};
wire [13:0] d_addr_next_h = d_addr[20:7] + d_addr_add_h;
wire [7:0] d_addr_next_l = (dma_dalgn && next_burst) ? d_addr_r : d_addr_inc_l[7:0];
wire d_addr_next_m = dma_dalgn ? (dma_asz ? d_addr_next_l[7] : d_addr_next_h[0]) : d_addr_inc_l[7];
wire [20:0] d_addr_next = {d_addr_next_h[13:1], d_addr_next_m, d_addr_next_l[6:0]};
always @(posedge clk) always @(posedge clk)
if ((dram_next || dev_stb) && state_wr) // increment RAM dest addr if ((dram_next || dev_stb) && state_wr) // increment RAM dest addr
d_addr <= d_addr_next; d_addr <= d_addr_next;
@ -328,10 +388,42 @@ module dma
end end
// INT generation // INT generation
reg dma_act_r; reg dma_act_r = 0;
always @(posedge clk) always @(posedge clk)
dma_act_r <= dma_act && ~reset; dma_act_r <= dma_act && rst_n;
assign int_start = !dma_act && dma_act_r; assign int_start = !dma_act && dma_act_r;
assign wraddr = d_addr[7:0];
// DRAM
assign dram_addr = state_rd ? ((!dv_blt || !phase_blt) ? s_addr : d_addr) : d_addr;
assign dram_wrdata = data;
assign dram_req = dma_act && state_mem;
assign dram_rnw = state_rd;
assign cram_we = dev_req && dv_crm && state_wr;
assign sfile_we = dev_req && dv_sfl && state_wr;
`ifdef FDR
// FDD
wire fdr_int_stb = dv_fdd && fdr_stb;
assign fdr_req = dev_req && dv_fdd;
`endif
// SPI
assign spi_int_stb = dv_spi && spi_stb;
assign spi_wrdata = {8{state_rd}} | (bsel ? data[15:8] : data[7:0]); // send FF on read cycles
assign spi_req = dev_req && dv_spi;
// WTPORT
assign wtp_int_stb = dv_wtp && wtp_stb;
assign wtp_req = dev_req && dv_wtp;
// IDE
assign ide_int_stb = dv_ide && ide_stb;
assign ide_out = data;
assign ide_req = dev_req && dv_ide;
assign ide_rnw = state_rd;
endmodule endmodule

15
rtl/common/resetter.v Normal file
View File

@ -0,0 +1,15 @@
`include "tune.v"
// Reset from MCU must be long enough
module resetter
(
input wire clk,
input wire rst_in_n, // external asynchronous reset
output reg rst_out_n // synchronized reset
);
always @(posedge clk)
rst_out_n <= rst_in_n;
endmodule

74
rtl/common/spi.v Normal file
View File

@ -0,0 +1,74 @@
`include "tune.v"
module spi
(
// SPI wires
input wire clk, // system clock
output wire sck, // SCK
output reg sdo, // MOSI
input wire sdi, // MISO
input wire mode, // 0 - CPHA=0, CPOL=0 / 1 - CPHA=1, CPOL=0
// DMA interface
input wire dma_req,
input wire [7:0] dma_din,
// Z80 interface
input wire cpu_req,
input wire [7:0] cpu_din,
// output
output wire start, // start strobe, 1 clock length
output reg [7:0] dout
);
reg [4:0] counter = 5'b10000;
reg [7:0] shift = 0;
reg busy_r;
wire busy = !counter[4];
wire req = cpu_req || dma_req;
assign sck = counter[0];
assign start = req && !busy;
wire [7:0] din = dma_req ? dma_din : cpu_din;
wire cpha = mode;
always @(posedge clk)
begin
busy_r <= busy;
if (start)
begin
counter <= 5'b0;
sdo <= din[7];
shift[7:1] <= din[6:0];
end
else
begin
if (!counter[4])
counter <= counter + 5'd1;
if (cpha ? busy_r : busy)
begin
// shift in
if (cpha ? sck : !sck)
begin
shift[0] <= sdi;
if (&counter[3:1])
dout <= {shift[7:1], sdi};
end
// shift out
if (cpha ? !sck : sck)
begin
sdo <= shift[7];
shift[7:1] <= shift[6:0]; // last bit remains after end of exchange
end
end
end
end
endmodule

View File

@ -1,106 +0,0 @@
// PentEvo project (c) NedoPC 2008-2011
//
// Z80 clocking module, also contains some wait-stating when 14MHz
//
// IDEAL:
// clk _/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zclk /```\___/```\___/```\___/```````\_______/```````\_______/```````````````\_______________/```````````````\_______________/`
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zpos `\___/```\___/```\___/```\___________/```\___________/```\___________________________/```\___________________________/```\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zneg _/```\___/```\___/```\_______/```\___________/```\___________________/```\___________________________/```\________________
// clock phasing:
// c3 must be zpos for 7mhz, therefore c1 - zneg
// for 3.5 mhz, c3 is both zpos and zneg (alternating)
// 14MHz rulez:
// 1. do variable stalls for memory access.
// 2. do fallback on 7mhz for external IO accesses
// 3. clock switch 14-7-3.5 only at RFSH
module zclock
(
input clk,
output zclk_out,
input c0, c2, f0, f1,
input iorq_s,
input external_port,
output reg zpos,
output reg zneg,
// stall enables and triggers
input cpu_stall,
input ide_stall,
input dos_on,
input vdos_off,
input [1:0] turbo // 2'b00 - 3.5 MHz
// 2'b01 - 7.0 MHz
// 2'b1x - 14.0 MHz
);
assign zclk_out = ~zclk_o;
reg zclk_o;
wire [1:0] turbo_int = turbo;
// wait generator
wire dos_io_stall = stall_start || !stall_count_end;
wire stall_start = dos_stall || io_stall;
wire dos_stall = dos_on || vdos_off;
wire io_stall = iorq_s && external_port && turbo_int[1];
wire stall_count_end = stall_count[3];
reg [3:0] stall_count;
always @(posedge clk) begin
if (stall_start) begin
if (dos_stall) stall_count <= 4; // 4 tacts 28MHz (1 tact 7MHz)
else if (io_stall) stall_count <= 0; // 8 tacts 28MHz (1 tact 3.5MHz)
end
else if (!stall_count_end) stall_count <= stall_count + 3'd1;
end
// Z80 clocking pre-strobes
wire pre_zpos = turbo_int[1] ? pre_zpos_140 : (turbo_int[0] ? pre_zpos_70 : pre_zpos_35);
wire pre_zneg = turbo_int[1] ? pre_zneg_140 : (turbo_int[0] ? pre_zneg_70 : pre_zneg_35);
wire pre_zpos_140 = f1;
wire pre_zneg_140 = f0;
wire pre_zpos_70 = c2;
wire pre_zneg_70 = c0;
wire pre_zpos_35 = c2_cnt && c2;
wire pre_zneg_35 = !c2_cnt && c2;
reg c2_cnt;
always @(posedge clk) if (c2) c2_cnt <= ~c2_cnt;
// Z80 clocking strobes
wire stall = cpu_stall || dos_io_stall || ide_stall;
always @(posedge clk) begin
zpos <= !stall && pre_zpos && zclk_o;
zneg <= !stall && pre_zneg && !zclk_o;
end
// make Z80 clock: account for external inversion and make some leading of clock
// 9.5 ns propagation delay: from clk posedge to zclk returned back any edge
// (1/28)/2=17.9ns half a clock lead
// 2.6ns lag because of non-output register emitting of zclk_o
// total: 5.8 ns lead of any edge of zclk relative to posedge of clk => ACCOUNT FOR THIS WHEN DOING INTER-CLOCK DATA TRANSFERS
// Z80 clocking
always @(negedge clk) begin
if (zpos) zclk_o <= 0;
if (zneg) zclk_o <= 1;
end
endmodule

View File

@ -1,71 +0,0 @@
module zint
(
input wire clk,
input wire zpos,
input wire res,
input wire int_start_frm,
input wire int_start_lin,
input wire int_start_dma,
input wire vdos,
input wire intack,
input wire [7:0] intmask,
output reg [7:0] im2vect,
output wire int_n
);
// In VDOS INTs are focibly disabled.
// For Frame, Line INT its generation is blocked, it will be lost.
// For DMA INT only its output is blocked, so DMA ISR will will be processed as soon as returned from VDOS.
assign int_n = ~(int_frm || int_lin || int_dma) | vdos;
wire dis_int_frm = !intmask[0];
wire dis_int_lin = !intmask[1];
wire dis_int_dma = !intmask[2];
wire intack_s = intack && !intack_r;
reg intack_r;
always @(posedge clk) intack_r <= intack;
reg [1:0] int_sel;
always @(posedge clk) begin
if (intack_s) begin
if (int_frm) im2vect <= 8'hFF; // priority 0
else if (int_lin) im2vect <= 8'hFD; // priority 1
else if (int_dma) im2vect <= 8'hFB; // priority 2
end
end
// ~INT generating
reg int_frm;
always @(posedge clk) begin
if (res || dis_int_frm) int_frm <= 0;
else if (int_start_frm) int_frm <= 1;
else if (intack_s || intctr_fin) int_frm <= 0; // priority 0
end
reg int_lin;
always @(posedge clk) begin
if (res || dis_int_lin) int_lin <= 0;
else if (int_start_lin) int_lin <= 1;
else if (intack_s && !int_frm) int_lin <= 0; // priority 1
end
reg int_dma;
always @(posedge clk) begin
if (res || dis_int_dma) int_dma <= 0;
else if (int_start_dma) int_dma <= 1;
else if (intack_s && !int_frm && !int_lin) int_dma <= 0; // priority 2
end
// ~INT counter
reg [5:0] intctr;
wire intctr_fin = intctr[5]; // 32 clks
always @(posedge clk, posedge int_start_frm) begin
if (int_start_frm) intctr <= 0;
else if (zpos && !intctr_fin && !vdos) intctr <= intctr + 1'b1;
end
endmodule

View File

@ -1,56 +0,0 @@
// This module maps z80 memory accesses into FPGA RAM and ports
module zmaps
(
// Z80 controls
input wire clk,
input wire memwr_s,
input wire [15:0] a,
input wire [7:0] d,
// config data
input wire [4:0] fmaddr,
// FPRAM data
output wire [15:0] zmd,
output wire [7:0] zma,
// DMA
input wire [15:0] dma_data,
input wire [7:0] dma_wraddr,
input wire dma_cram_we,
input wire dma_sfile_we,
// write strobes
output wire cram_we,
output wire sfile_we,
output wire regs_we
);
// addresses of files withing zmaps
localparam CRAM = 3'b000;
localparam SFYS = 3'b001;
localparam REGS = 4'b0100;
// control signals
wire hit = (a[15:12] == fmaddr[3:0]) && fmaddr[4] && memwr_s;
// write enables
assign cram_we = dma_req ? dma_cram_we : (a[11:9] == CRAM) && a[0] && hit;
assign sfile_we = dma_req ? dma_sfile_we : (a[11:9] == SFYS) && a[0] && hit;
assign regs_we = (a[11:8] == REGS) && hit;
// LSB fetching
assign zma = dma_req ? dma_wraddr : a[8:1];
assign zmd = dma_req ? dma_data : {d, zmd0};
reg [7:0] zmd0;
always @(posedge clk) if (!a[0] && hit) zmd0 <= d;
// DMA
wire dma_req = dma_cram_we || dma_sfile_we;
endmodule

View File

@ -1,228 +0,0 @@
// PentEvo project (c) NedoPC 2008-2009
//
// Z80 memory manager: routes ROM/RAM accesses, makes wait-states for 14MHz or stall condition, etc.
//
//
// clk _/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zclk /```\___/```\___/```\___/```````\_______/```````\_______/```````````````\_______________/```````````````\_______________/`
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zpos `\___/```\___/```\___/```\___________/```\___________/```\___________________________/```\___________________________/```\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zneg _/```\___/```\___/```\_______/```\___________/```\___________________/```\___________________________/```\________________
module zmem
(
input clk,
input c0, c1, c2, c3,
input zneg, // strobes which show positive and negative edges of zclk
input zpos,
// Z80
input rst,
input [15:0] za,
output [ 7:0] zd_out, // output to Z80 bus
output zd_ena, // output to Z80 bus enable
input opfetch,
input opfetch_s,
input mreq,
input memrd,
input memwr,
input memwr_s,
input [ 1:0] turbo, // 2'b00 - 3.5,
// 2'b01 - 7.0,
// 2'b1x - 14.0
input [3:0] cache_en,
input [3:0] memconf,
input [31:0] xt_page,
output [4:0] rompg,
output csrom,
output romoe_n,
output romwe_n,
output dos,
output dos_on,
output dos_off,
output vdos,
output reg pre_vdos,
input vdos_on,
input vdos_off,
// DRAM
output cpu_req,
output [20:0] cpu_addr,
output cpu_wrbsel,
input [15:0] cpu_rddata,
input cpu_next,
input cpu_strobe,
input cpu_latch,
output cpu_stall // for zclock
);
// controls
wire rom128 = memconf[0];
wire w0_we = memconf[1];
wire w0_map_n = memconf[2];
wire w0_ram = memconf[3];
// pager
wire [1:0] win = za[15:14];
wire win0 = ~|win;
wire ramwr_en = !win0 || w0_we || vdos;
wire rom_n_ram = win0 && !w0_ram && !vdos;
wire [7:0] page = xtpage[win];
wire [7:0] xtpage[0:3];
assign xtpage[0] = vdos ? 8'hFF : {xt_page[7:2], w0_map_n ? xt_page[1:0] : {~dos, rom128}};
assign xtpage[1] = xt_page[15:8];
assign xtpage[2] = xt_page[23:16];
assign xtpage[3] = xt_page[31:24];
// ROM chip
assign csrom = rom_n_ram;
assign romoe_n = !memrd;
assign romwe_n = !(memwr && w0_we);
assign rompg = xtpage[0][4:0];
// RAM
assign zd_ena = memrd;
wire ramreq = ((memrd && !cache_hit_en) || (memwr && ramwr_en));
// DOS signal control
assign dos_on = win0 && opfetch_s && (za[13:8]==6'h3D) && rom128 && !w0_map_n;
assign dos_off = !win0 && opfetch_s && !vdos;
assign dos = (dos_on || dos_off) ^^ dos_r; // to make dos appear 1 clock earlier than dos_r
reg dos_r;
always @(posedge clk) begin
if (rst) dos_r <= 0;
else if (dos_off) dos_r <= 0;
else if (dos_on) dos_r <= 1;
end
// VDOS signal control
// vdos turn on/off is delayed till next opfetch due to INIR that writes right after iord cycle
assign vdos = opfetch ? pre_vdos : vdos_r; // vdos appears as soon as first opfetch
reg vdos_r;
always @(posedge clk) begin
if (rst || vdos_off) begin
pre_vdos <= 0;
vdos_r <= 0;
end
else if (vdos_on) pre_vdos <= 1;
else if (opfetch_s) vdos_r <= pre_vdos;
end
// address, data in and data out
assign cpu_wrbsel = za[0];
assign cpu_addr[20:0] = {page, za[13:1]};
wire [15:0] mem_d = cpu_latch ? cpu_rddata : cache_d;
assign zd_out = ~cpu_wrbsel ? mem_d[7:0] : mem_d[15:8];
// Z80 controls
assign cpu_req = turbo14 ? cpureq_14 : cpureq_357;
assign cpu_stall = turbo14 ? stall14 : stall357;
wire turbo14 = turbo[1];
// 7/3.5MHz support
wire cpureq_357 = ramreq && !ramreq_r;
wire stall357 = cpureq_357 && !cpu_next;
reg ramreq_r;
always @(posedge clk) if (c3 && !cpu_stall) ramreq_r <= ramreq;
// 14MHz support
// wait tables:
//
// M1 opcode fetch, dram_beg concurs with:
// c3: +3
// c2: +4
// c1: +5
// c0: +6
//
// memory read, dram_beg concurs with:
// c3: +2
// c2: +3
// c1: +4
// c0: +5
//
// memory write: no wait
//
// special case: if dram_beg pulses 1 when cpu_next is 0,
// unconditional wait has to be performed until cpu_next is 1, and
// then wait as if dram_beg would concur with c0
// memrd, opfetch - wait till c3 && cpu_next,
// memwr - wait till cpu_next
wire cpureq_14 = dram_beg || pending_cpu_req;
wire stall14 = stall14_ini || stall14_cyc || stall14_fin;
wire dram_beg = ramreq && !pre_ramreq_r && zneg;
reg pre_ramreq_r;
always @(posedge clk) if (zneg) pre_ramreq_r <= ramreq;
reg pending_cpu_req;
always @(posedge clk) begin
if (rst) pending_cpu_req <= 0;
else if (cpu_next && c3) pending_cpu_req <= 0;
else if (dram_beg) pending_cpu_req <= 1;
end
wire stall14_ini = dram_beg && (!cpu_next || opfetch || memrd); // no wait at all in write cycles, if next dram cycle is available
wire stall14_cyc = memrd ? stall14_cycrd : !cpu_next;
reg stall14_cycrd;
always @(posedge clk) begin
if (rst) stall14_cycrd <= 0;
else if (cpu_next && c3) stall14_cycrd <= 0;
else if (dram_beg && (!c3 || !cpu_next) && (opfetch || memrd)) stall14_cycrd <= 1;
end
reg stall14_fin;
always @(posedge clk) begin
if (rst) stall14_fin <= 0;
else if (stall14_fin && ((opfetch && c1) || (memrd && c2))) stall14_fin <= 0;
else if (cpu_next && c3 && cpu_req && (opfetch || memrd)) stall14_fin <= 1;
end
// cache
// wire cache_hit = (ch_addr[7:2] != 6'b011100) && (cpu_hi_addr == cache_a) && cache_v; // debug for BM
wire cache_hit = (cpu_hi_addr == cache_a) && cache_v; // asynchronous signal meaning that address requested by CPU is cached and valid
wire cache_hit_en = cache_hit && cache_en[win];
wire cache_inv = cache_hit && memwr_s && ramwr_en; // cache invalidation should be only performed if write happens to cached address
wire [12:0] cpu_hi_addr = {page[7:0], za[13:9]};
wire [12:0] cache_a;
wire [7:0] ch_addr = cpu_addr[7:0];
wire [15:0] cache_d;
wire cache_v;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) cache_data
(
.clock(clk),
.address_a(ch_addr),
.data_a(cpu_rddata),
.wren_a(cpu_strobe),
.address_b(ch_addr),
.q_b(cache_d)
);
dpram #(.DATAWIDTH(14), .ADDRWIDTH(8)) cache_addr
(
.clock(clk),
.address_a(ch_addr),
.data_a({!cache_inv, cpu_hi_addr}),
.wren_a(cpu_strobe || cache_inv),
.address_b(ch_addr),
.q_b({cache_v, cache_a})
);
endmodule

View File

@ -1,469 +0,0 @@
// PentEvo project (c) NedoPC 2008-2010
module zports
(
input clk,
input [7:0] din,
output reg [7:0] dout,
output dataout,
input [15:0] a,
input rst, // system reset
input opfetch,
input rd,
input wr,
input rdwr,
input iorq,
input iorq_s,
input iord,
input iord_s,
input iowr,
input iowr_s,
input iordwr,
input iordwr_s,
output porthit, // when internal port hit occurs, this is 1, else 0; used for iorq1_n iorq2_n on zxbus
output external_port, // asserts for AY and VG93 accesses
output zborder_wr,
output border_wr,
output zvpage_wr,
output vpage_wr,
output vconf_wr,
output gx_offsl_wr,
output gx_offsh_wr,
output gy_offsl_wr,
output gy_offsh_wr,
output t0x_offsl_wr,
output t0x_offsh_wr,
output t0y_offsl_wr,
output t0y_offsh_wr,
output t1x_offsl_wr,
output t1x_offsh_wr,
output t1y_offsl_wr,
output t1y_offsh_wr,
output tsconf_wr,
output palsel_wr,
output tmpage_wr,
output t0gpage_wr,
output t1gpage_wr,
output sgpage_wr,
output hint_beg_wr ,
output vint_begl_wr,
output vint_begh_wr,
output [31:0] xt_page,
output reg [4:0] fmaddr,
input regs_we,
output reg [7:0] sysconf,
output reg [7:0] memconf,
output reg [3:0] cacheconf,
output reg [7:0] fddvirt,
output [8:0] dmaport_wr,
input dma_act,
output reg [1:0] dmawpdev,
output reg [7:0] intmask,
input dos,
input vdos,
output vdos_on,
output vdos_off,
output ay_bdir,
output ay_bc1,
output covox_wr,
output beeper_wr,
input tape_read,
input [4:0] keys_in, // keys (port FE)
input [7:0] mus_in, // mouse (xxDF)
input [5:0] kj_in,
input vg_intrq,
input vg_drq, // from vg93 module - drq + irq read
output vg_cs_n,
output vg_wrFF,
output [1:0] drive_sel, // disk drive selection
// SPI
output sdcs_n,
output sd_start,
output [7:0] sd_datain,
input [7:0] sd_dataout,
// WAIT-ports related
output reg [7:0] wait_addr,
output wait_start_gluclock, // begin wait from some ports
output wait_start_comport, //
output reg [7:0] wait_write,
input [7:0] wait_read
);
assign sdcs_n = spi_cs_n[0];
localparam FDR_VER = 1'b0;
localparam VDAC_VER = 3'h3;
localparam PORTFE = 8'hFE;
localparam PORTFD = 8'hFD;
localparam PORTXT = 8'hAF;
localparam PORTF7 = 8'hF7;
localparam COVOX = 8'hFB;
localparam VGCOM = 8'h1F;
localparam VGTRK = 8'h3F;
localparam VGSEC = 8'h5F;
localparam VGDAT = 8'h7F;
localparam VGSYS = 8'hFF;
localparam KJOY = 8'h1F;
localparam KMOUSE = 8'hDF;
localparam SDCFG = 8'h77;
localparam SDDAT = 8'h57;
localparam COMPORT = 8'hEF; // F8EF..FFEF - rs232 ports
wire [7:0] loa = a[7:0];
wire [7:0] hoa = regs_we ? a[7:0] : a[15:8];
assign porthit = ((loa==PORTFE) || (loa==PORTXT) || (loa==PORTFD) || (loa==COVOX))
|| ((loa==PORTF7) && !dos)
|| ((vg_port || vgsys_port) && (dos || open_vg))
|| ((loa==KJOY) && !dos && !open_vg)
|| (loa==KMOUSE)
|| (((loa==SDCFG) || (loa==SDDAT)) && (!dos || vdos))
|| (loa==COMPORT);
wire vg_port = (loa==VGCOM) || (loa==VGTRK) || (loa==VGSEC) || (loa==VGDAT);
wire vgsys_port = (loa==VGSYS);
assign external_port = ((loa==PORTFD) && a[15]) // AY
|| (((loa==VGCOM) || (loa==VGTRK) || (loa==VGSEC) || (loa==VGDAT)) && (dos || open_vg));
assign dataout = porthit && iord && (~external_port);
reg iowr_reg;
reg iord_reg;
reg port_wr;
reg port_rd;
always @(posedge clk) begin
iowr_reg <= iowr;
port_wr <= (!iowr_reg && iowr);
iord_reg <= iord;
port_rd <= (!iord_reg && iord);
end
// reading ports
always @(*) begin
case (loa)
PORTFE:
dout = {1'b1, tape_read, 1'b0, keys_in};
PORTXT:
begin
case (hoa)
XSTAT:
dout = {1'b0, pwr_up_reg, FDR_VER, 2'b0, VDAC_VER};
DMASTAT:
dout = {dma_act, 7'b0};
RAMPAGE + 8'd2, RAMPAGE + 8'd3:
dout = rampage[hoa[1:0]];
default:
dout = 8'hFF;
endcase
end
VGSYS:
dout = {vg_intrq, vg_drq, 6'b111111};
KJOY:
dout = {2'b00, kj_in};
KMOUSE:
dout = mus_in;
SDCFG:
dout = 8'h00; // always SD inserted, SD is in R/W mode
SDDAT:
dout = sd_dataout;
PORTF7:
begin
if (!a[14] && (a[8] ^ dos) && gluclock_on) dout = wait_read; // $BFF7 - data i/o
else dout = 8'hFF; // any other $xxF7 port
end
COMPORT:
dout = wait_read; // $F8EF..$FFEF
default:
dout = 8'hFF;
endcase
end
// power-up
// This bit is loaded as 1 while FPGA is configured
// and automatically reset to 0 after STATUS port reading
reg pwr_up_reg;
reg pwr_up = 1;
always @(posedge clk) begin
if (iord_s & (loa == PORTXT) & (hoa == XSTAT)) begin
pwr_up_reg <= pwr_up;
pwr_up <= 1'b0;
end
end
// writing ports
//#nnAF
localparam VCONF = 8'h00;
localparam VPAGE = 8'h01;
localparam GXOFFSL = 8'h02;
localparam GXOFFSH = 8'h03;
localparam GYOFFSL = 8'h04;
localparam GYOFFSH = 8'h05;
localparam TSCONF = 8'h06;
localparam PALSEL = 8'h07;
localparam XBORDER = 8'h0F;
localparam T0XOFFSL = 8'h40;
localparam T0XOFFSH = 8'h41;
localparam T0YOFFSL = 8'h42;
localparam T0YOFFSH = 8'h43;
localparam T1XOFFSL = 8'h44;
localparam T1XOFFSH = 8'h45;
localparam T1YOFFSL = 8'h46;
localparam T1YOFFSH = 8'h47;
localparam RAMPAGE = 8'h10; // this covers #10-#13
localparam FMADDR = 8'h15;
localparam TMPAGE = 8'h16;
localparam T0GPAGE = 8'h17;
localparam T1GPAGE = 8'h18;
localparam SGPAGE = 8'h19;
localparam DMASADDRL = 8'h1A;
localparam DMASADDRH = 8'h1B;
localparam DMASADDRX = 8'h1C;
localparam DMADADDRL = 8'h1D;
localparam DMADADDRH = 8'h1E;
localparam DMADADDRX = 8'h1F;
localparam SYSCONF = 8'h20;
localparam MEMCONF = 8'h21;
localparam HSINT = 8'h22;
localparam VSINTL = 8'h23;
localparam VSINTH = 8'h24;
localparam DMAWPD = 8'h25;
localparam DMALEN = 8'h26;
localparam DMACTRL = 8'h27;
localparam DMANUM = 8'h28;
localparam FDDVIRT = 8'h29;
localparam INTMASK = 8'h2A;
localparam CACHECONF = 8'h2B;
localparam DMAWPA = 8'h2D;
localparam XSTAT = 8'h00;
localparam DMASTAT = 8'h27;
assign dmaport_wr[0] = portxt_wr && (hoa == DMASADDRL);
assign dmaport_wr[1] = portxt_wr && (hoa == DMASADDRH);
assign dmaport_wr[2] = portxt_wr && (hoa == DMASADDRX);
assign dmaport_wr[3] = portxt_wr && (hoa == DMADADDRL);
assign dmaport_wr[4] = portxt_wr && (hoa == DMADADDRH);
assign dmaport_wr[5] = portxt_wr && (hoa == DMADADDRX);
assign dmaport_wr[6] = portxt_wr && (hoa == DMALEN);
assign dmaport_wr[7] = portxt_wr && (hoa == DMACTRL);
assign dmaport_wr[8] = portxt_wr && (hoa == DMANUM);
assign zborder_wr = portfe_wr;
assign border_wr = (portxt_wr && (hoa == XBORDER));
assign zvpage_wr = p7ffd_wr;
assign vpage_wr = (portxt_wr && (hoa == VPAGE ));
assign vconf_wr = (portxt_wr && (hoa == VCONF ));
assign gx_offsl_wr = (portxt_wr && (hoa == GXOFFSL));
assign gx_offsh_wr = (portxt_wr && (hoa == GXOFFSH));
assign gy_offsl_wr = (portxt_wr && (hoa == GYOFFSL));
assign gy_offsh_wr = (portxt_wr && (hoa == GYOFFSH));
assign t0x_offsl_wr = (portxt_wr && (hoa == T0XOFFSL));
assign t0x_offsh_wr = (portxt_wr && (hoa == T0XOFFSH));
assign t0y_offsl_wr = (portxt_wr && (hoa == T0YOFFSL));
assign t0y_offsh_wr = (portxt_wr && (hoa == T0YOFFSH));
assign t1x_offsl_wr = (portxt_wr && (hoa == T1XOFFSL));
assign t1x_offsh_wr = (portxt_wr && (hoa == T1XOFFSH));
assign t1y_offsl_wr = (portxt_wr && (hoa == T1YOFFSL));
assign t1y_offsh_wr = (portxt_wr && (hoa == T1YOFFSH));
assign tsconf_wr = (portxt_wr && (hoa == TSCONF));
assign palsel_wr = (portxt_wr && (hoa == PALSEL));
assign tmpage_wr = (portxt_wr && (hoa == TMPAGE));
assign t0gpage_wr = (portxt_wr && (hoa == T0GPAGE));
assign t1gpage_wr = (portxt_wr && (hoa == T1GPAGE));
assign sgpage_wr = (portxt_wr && (hoa == SGPAGE));
assign hint_beg_wr = (portxt_wr && (hoa == HSINT ));
assign vint_begl_wr = (portxt_wr && (hoa == VSINTL));
assign vint_begh_wr = (portxt_wr && (hoa == VSINTH));
assign beeper_wr = portfe_wr;
wire portfe_wr = (loa==PORTFE) && iowr_s;
assign covox_wr = (loa==COVOX) && iowr_s;
wire portxt_wr = ((loa==PORTXT) && iowr_s) || regs_we;
reg [7:0] rampage[0:3];
assign xt_page = {rampage[3], rampage[2], rampage[1], rampage[0]};
wire lock128 = lock128_3 ? 1'b0 : (lock128_2 ? m1_lock128 : memconf[6]);
wire lock128_2 = memconf[7:6] == 2'b10; // mode 2
wire lock128_3 = memconf[7:6] == 2'b11; // mode 3
reg m1_lock128;
always @(posedge clk) if (opfetch) m1_lock128 <= !(din[7] ^ din[6]);
always @(posedge clk) begin
if (rst) begin
fmaddr[4] <= 1'b0;
intmask <= 8'b1;
fddvirt <= 8'b0;
sysconf <= 8'h00; // 3.5 MHz
memconf <= 8'h04; // no map
cacheconf <= 4'h0; // no cache
rampage[0]<= 8'h00;
rampage[1]<= 8'h05;
rampage[2]<= 8'h02;
rampage[3]<= 8'h00;
end
else if (p7ffd_wr) begin
memconf[0] <= din[4];
rampage[3] <= {2'b0, lock128_3 ? {din[5], din[7:6]} : ({1'b0, lock128 ? 2'b0 : din[7:6]}), din[2:0]};
end
else if (portxt_wr) begin
if (hoa[7:2] == RAMPAGE[7:2]) rampage[hoa[1:0]] <= din;
if (hoa == FMADDR) fmaddr <= din[4:0];
if (hoa == SYSCONF) begin
sysconf <= din;
cacheconf <= {4{din[2]}};
end
if (hoa == DMAWPD) dmawpdev <= din[1:0];
if (hoa == CACHECONF) cacheconf <= din[3:0];
if (hoa == MEMCONF) memconf <= din;
if (hoa == FDDVIRT) fddvirt <= din;
if (hoa == INTMASK) intmask <= din;
end
end
// 7FFD port
wire p7ffd_wr = !a[15] && (loa==PORTFD) && iowr_s && !lock48;
reg lock48;
always @(posedge clk) begin
if (rst) lock48 <= 1'b0;
else if (p7ffd_wr && !lock128_3) lock48 <= din[5];
end
// AY control
wire ay_hit = (loa==PORTFD) & a[15];
assign ay_bc1 = ay_hit & a[14] & iordwr;
assign ay_bdir = ay_hit & iowr;
// VG93
wire [3:0] fddvrt = fddvirt[3:0];
wire virt_vg = fddvrt[drive_sel_raw];
wire open_vg = fddvirt[7];
assign drive_sel = {drive_sel_raw[1], drive_sel_raw[0]};
wire vg_wen = (dos || open_vg) && !vdos && !virt_vg;
assign vg_cs_n = !(iordwr && vg_port && vg_wen);
assign vg_wrFF = iowr_s && vgsys_port && vg_wen;
wire vg_wrDS = iowr_s && vgsys_port && (dos || open_vg);
assign vdos_on = iordwr_s && (vg_port || vgsys_port) && dos && !vdos && virt_vg;
assign vdos_off = iordwr_s && vg_port && vdos;
// write drive number
reg [1:0] drive_sel_raw;
always @(posedge clk) if (vg_wrDS) drive_sel_raw <= din[1:0];
// SD card (Z-controller compatible)
wire sdcfg_wr;
wire sddat_wr;
wire sddat_rd;
reg [1:0] spi_cs_n;
assign sdcfg_wr = ((loa==SDCFG) && iowr_s && (!dos || vdos));
assign sddat_wr = ((loa==SDDAT) && iowr_s && (!dos || vdos));
assign sddat_rd = ((loa==SDDAT) && iord_s);
// SDCFG write - sdcs_n control
always @(posedge clk) begin
if (rst) spi_cs_n <= 2'b11;
else if (sdcfg_wr) spi_cs_n <= {~din[2], din[1]};
end
// start signal for SPI module with resyncing to fclk
assign sd_start = sddat_wr || sddat_rd;
// data for SPI module
assign sd_datain = wr ? din : 8'hFF;
// xxF7
wire portf7_wr = ((loa==PORTF7) && (a[8]==1'b1) && port_wr && (!dos || vdos));
wire portf7_rd = ((loa==PORTF7) && (a[8]==1'b1) && port_rd && (!dos || vdos));
// EFF7 port
reg [7:0] peff7;
always @(posedge clk) begin
if (rst) peff7 <= 8'h00;
else if (!a[12] && portf7_wr && !dos) peff7 <= din; // #EEF7 in dos is not accessible
end
// gluclock ports
wire gluclock_on = peff7[7] || dos; // in dos mode EEF7 is not accessible, gluclock access is ON in dos mode.
// comports
wire comport_wr = ((loa == COMPORT) && port_wr);
wire comport_rd = ((loa == COMPORT) && port_rd);
// write to wait registers
always @(posedge clk) begin
// gluclocks
if (gluclock_on && portf7_wr) begin
if (!a[14]) wait_write <= din; // $BFF7 - data reg
if (!a[13]) wait_addr <= din; // $DFF7 - addr reg
end
// com ports
if (comport_wr) wait_write <= din; // $xxEF
if (comport_wr || comport_rd) wait_addr <= a[15:8];
if ((loa==PORTXT) && (hoa == DMAWPA)) wait_addr <= din;
end
// wait from wait registers
// ACHTUNG!!!! here portxx_wr are ON Z80 CLOCK! logic must change when moving to clk strobes
assign wait_start_gluclock = (gluclock_on && !a[14] && (portf7_rd || portf7_wr)); // $BFF7 - gluclock r/w
assign wait_start_comport = (comport_rd || comport_wr);
endmodule

View File

@ -1,85 +0,0 @@
// Decoding and strobing of z80 signals
module zsignals
(
// clocks
input wire clk,
// z80 interface input
input wire iorq_n,
input wire mreq_n,
input wire m1_n,
input wire rfsh_n,
input wire rd_n,
input wire wr_n,
// Z80 signals
output wire m1,
output wire rfsh,
output wire rd,
output wire wr,
output wire iorq,
output wire mreq,
output wire rdwr,
output wire iord,
output wire iowr,
output wire iorw,
output wire memrd,
output wire memwr,
output wire memrw,
output wire opfetch,
output wire intack,
// Z80 signals strobes, at fclk
output wire iorq_s,
output wire mreq_s,
output wire iord_s,
output wire iowr_s,
output wire iorw_s,
output wire memrd_s,
output wire memwr_s,
output wire memrw_s,
output wire opfetch_s
);
// invertors
assign m1 = !m1_n;
assign rfsh = !rfsh_n;
assign rd = !rd_n;
assign wr = !wr_n;
// requests
assign iorq = !iorq_n && m1_n; // this is masked by ~M1 to avoid port decoding on INT ack
assign mreq = !mreq_n && rfsh_n; // this is masked by ~RFSH to ignore refresh cycles as memory requests
// combined
assign rdwr = rd || wr;
assign iord = iorq && rd;
assign iowr = iorq && wr;
assign iorw = iorq && rdwr;
assign memrd = mreq && rd;
assign memwr = mreq && !rd;
assign memrw = mreq && rdwr;
assign opfetch = memrd && m1;
assign intack = !iorq_n && m1; // NOT masked by M1
// strobed
assign iorq_s = iorq_r[0] && !iorq_r[1];
assign mreq_s = mreq_r[0] && !mreq_r[1];
assign iord_s = iorq_s && rd;
assign iowr_s = iorq_s && wr;
assign iorw_s = iorq_s && rdwr;
assign memrd_s = mreq_s && rd;
assign memwr_s = mreq_s && !rd;
assign memrw_s = mreq_s && rdwr;
assign opfetch_s = memrd_s && m1;
// latch inputs on FPGA clock
reg [1:0] iorq_r, mreq_r;
always @(posedge clk) begin
iorq_r <= {iorq_r[0], iorq};
mreq_r <= {mreq_r[0], mreq};
end
endmodule

267
rtl/dram/arbiter.v Normal file
View File

@ -0,0 +1,267 @@
`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 [21: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
);
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;
always @(posedge loader_clk) begin
if (loader_wr) begin
loader_wr0 <= 1'd1;
loader_data0 <= loader_data;
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 = {22{next_loader}} & { 1'b1, 6'b000000, loader_addr[15:1] }
| {22{next_cpu}} & { cpu_csrom, {6{~cpu_csrom}} & cpu_addr[20:15], cpu_addr[14:0] }
| {22{next_vid}} & { 1'b0, video_addr }
| {22{next_ts}} & { 1'b0, ts_addr }
| {22{next_tm}} & { 1'b0, tm_addr }
| {22{next_dma}} & { 1'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

View File

@ -1,72 +1,72 @@
module dpram #(parameter DATAWIDTH=8, ADDRWIDTH=8, NUMWORDS=1<<ADDRWIDTH, MEM_INIT_FILE="") module dpram #(parameter DATAWIDTH=8, ADDRWIDTH=8, NUMWORDS=1<<ADDRWIDTH, MEM_INIT_FILE="")
( (
input clock, input clock,
input [ADDRWIDTH-1:0] address_a, input [ADDRWIDTH-1:0] address_a,
input [DATAWIDTH-1:0] data_a, input [DATAWIDTH-1:0] data_a,
input wren_a, input wren_a,
output [DATAWIDTH-1:0] q_a, output [DATAWIDTH-1:0] q_a,
input [ADDRWIDTH-1:0] address_b, input [ADDRWIDTH-1:0] address_b,
input [DATAWIDTH-1:0] data_b, input [DATAWIDTH-1:0] data_b,
input wren_b, input wren_b,
output [DATAWIDTH-1:0] q_b output [DATAWIDTH-1:0] q_b
); );
altsyncram altsyncram_component ( altsyncram altsyncram_component (
.address_a (address_a), .address_a (address_a),
.address_b (address_b), .address_b (address_b),
.clock0 (clock), .clock0 (clock),
.data_a (data_a), .data_a (data_a),
.data_b (data_b), .data_b (data_b),
.wren_a (wren_a), .wren_a (wren_a),
.wren_b (wren_b), .wren_b (wren_b),
.q_a (q_a), .q_a (q_a),
.q_b (q_b), .q_b (q_b),
.aclr0 (1'b0), .aclr0 (1'b0),
.aclr1 (1'b0), .aclr1 (1'b0),
.addressstall_a (1'b0), .addressstall_a (1'b0),
.addressstall_b (1'b0), .addressstall_b (1'b0),
.byteena_a (1'b1), .byteena_a (1'b1),
.byteena_b (1'b1), .byteena_b (1'b1),
.clock1 (1'b1), .clock1 (1'b1),
.clocken0 (1'b1), .clocken0 (1'b1),
.clocken1 (1'b1), .clocken1 (1'b1),
.clocken2 (1'b1), .clocken2 (1'b1),
.clocken3 (1'b1), .clocken3 (1'b1),
.eccstatus (), .eccstatus (),
.rden_a (1'b1), .rden_a (1'b1),
.rden_b (1'b1)); .rden_b (1'b1));
defparam defparam
altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK0", altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK0",
altsyncram_component.address_reg_b = "CLOCK0", altsyncram_component.address_reg_b = "CLOCK0",
altsyncram_component.indata_reg_b = "CLOCK0", altsyncram_component.indata_reg_b = "CLOCK0",
altsyncram_component.numwords_a = NUMWORDS, altsyncram_component.numwords_a = NUMWORDS,
altsyncram_component.numwords_b = NUMWORDS, altsyncram_component.numwords_b = NUMWORDS,
altsyncram_component.widthad_a = ADDRWIDTH, altsyncram_component.widthad_a = ADDRWIDTH,
altsyncram_component.widthad_b = ADDRWIDTH, altsyncram_component.widthad_b = ADDRWIDTH,
altsyncram_component.width_a = DATAWIDTH, altsyncram_component.width_a = DATAWIDTH,
altsyncram_component.width_b = DATAWIDTH, altsyncram_component.width_b = DATAWIDTH,
altsyncram_component.width_byteena_a = 1, altsyncram_component.width_byteena_a = 1,
altsyncram_component.width_byteena_b = 1, altsyncram_component.width_byteena_b = 1,
altsyncram_component.init_file = MEM_INIT_FILE, altsyncram_component.init_file = MEM_INIT_FILE,
altsyncram_component.clock_enable_input_a = "BYPASS", altsyncram_component.clock_enable_input_a = "BYPASS",
altsyncram_component.clock_enable_input_b = "BYPASS", altsyncram_component.clock_enable_input_b = "BYPASS",
altsyncram_component.clock_enable_output_a = "BYPASS", altsyncram_component.clock_enable_output_a = "BYPASS",
altsyncram_component.clock_enable_output_b = "BYPASS", altsyncram_component.clock_enable_output_b = "BYPASS",
altsyncram_component.intended_device_family = "Cyclone III", altsyncram_component.intended_device_family = "Cyclone III",
altsyncram_component.lpm_type = "altsyncram", altsyncram_component.lpm_type = "altsyncram",
altsyncram_component.operation_mode = "BIDIR_DUAL_PORT", altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
altsyncram_component.outdata_aclr_a = "NONE", altsyncram_component.outdata_aclr_a = "NONE",
altsyncram_component.outdata_aclr_b = "NONE", altsyncram_component.outdata_aclr_b = "NONE",
altsyncram_component.outdata_reg_a = "UNREGISTERED", altsyncram_component.outdata_reg_a = "UNREGISTERED",
altsyncram_component.outdata_reg_b = "UNREGISTERED", altsyncram_component.outdata_reg_b = "UNREGISTERED",
altsyncram_component.power_up_uninitialized = "FALSE", altsyncram_component.power_up_uninitialized = "FALSE",
altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE", altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ", altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_NO_NBE_READ",
altsyncram_component.read_during_write_mode_port_b = "NEW_DATA_NO_NBE_READ"; altsyncram_component.read_during_write_mode_port_b = "NEW_DATA_NO_NBE_READ";
endmodule endmodule

View File

@ -1,263 +0,0 @@
// 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 go back-to-back
//
// key signals are 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 clk,
input c0,
input c1,
input c2,
input c3,
input cyc,
// dram.v interface
output [21:0] dram_addr, // address for dram access
output dram_req, // dram request
output dram_rnw, // Read-NotWrite
output [ 1:0] dram_bsel, // byte select: bsel[1] for wrdata[15:8], bsel[0] for wrdata[7:0]
output [15:0] dram_wrdata, // data to be written
// video
input [20:0] video_addr, // during access block, only when video_strobe==1
input go, // start video access blocks
input [ 4:0] video_bw, // [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
// [2:0] - need cycles
output video_pre_next, // (c1)
output video_next, // (c2) at this signal video_addr may be changed; it is one clock leading the video_strobe
output video_strobe, // (c3) one-cycle strobe meaning that video_data is available
output video_next_strobe, // (c3) one-cycle strobe meaning that video_data is available
output next_vid, // used for TM prefetch
// CPU
input [20:0] cpu_addr,
input [ 7:0] cpu_wrdata,
input cpu_req,
input cpu_rnw,
input cpu_csrom,
input cpu_wrbsel,
output reg cpu_next, // next cycle is allowed to be used by CPU
output reg cpu_strobe, // c2 strobe
output reg cpu_latch, // c2-c3 strobe
output curr_cpu_o,
// DMA
input [20:0] dma_addr,
input [15:0] dma_wrdata,
input dma_req,
input dma_rnw,
output dma_next,
// TS
input [20:0] ts_addr,
input ts_req,
output ts_pre_next,
output ts_next,
// TM
input [20:0] tm_addr,
input tm_req,
output tm_next,
// ROM loader
input loader_clk,
input [15:0] loader_addr,
input [7:0] loader_data,
input loader_wr
);
assign curr_cpu_o = curr_cpu;
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
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];
// track blk_rem counter:
// how many cycles left to the end of block (7..0)
wire [2:0] blk_nrem = (video_start && 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_start = ~|blk_rem;
wire video_only = stall || (vid_rem == blk_rem);
wire video_idle = ~|vid_rem;
reg [2:0] blk_rem; // remaining accesses in a block (7..0)
reg stall;
always @(posedge clk) begin
if (c3) begin
blk_rem <= blk_nrem;
if (video_start) stall <= bw_full & go;
end
end
// track vid_rem counter
// how many video cycles left to the end of block (7..0)
wire [2:0] vid_nrem = (go && video_start) ? vid_nrem_start : (next_vid ? vid_nrem_next : vid_rem);
wire [2:0] vid_nrem_start = (cpu_req && !dev_over_cpu) ? vidmax : (vidmax - 3'd1);
wire [2:0] vid_nrem_next = video_idle ? 3'd0 : (vid_rem - 3'd1);
wire [2:0] vidmax = {video_bw[2:0]}; // number of cycles for video access
reg [2:0] vid_rem; // remaining video accesses in block
always @(posedge clk) if (c3) vid_rem <= vid_nrem;
reg loader_wr0;
reg [7:0] loader_data0;
always @(posedge loader_clk) begin
if (loader_wr) begin
loader_wr0 <= 1'd1;
loader_data0 <= loader_data;
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;
// wire dev_over_cpu = (((ts_req || tm_req) && ts_z80_lp) || (dma_req && dma_z80_lp)) && int_n; // CPU gets higher priority to acknowledge the INT
wire dev_over_cpu = 0;
always @* begin
if (loader_wr0) begin
cpu_next = 1'b0;
next_cycle = CYC_LOADER;
end
else if (video_start) begin // video burst start
if (go) begin // video active line - 38us-ON, 26us-ON
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 begin // video idle
cpu_next = !dev_over_cpu;
next_cycle = dev_over_cpu ? cyc_dev : (cpu_req ? CYC_CPU : (dev_req ? cyc_dev : CYC_FREE));
end
end
else begin // video burst in progress
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
end
always @(posedge clk) if (c3) curr_cycle <= next_cycle;
// DRAM interface
assign dram_wrdata= curr_loader? {loader_data0,loader_data0} : curr_dma ? dma_wrdata : {cpu_wrdata,cpu_wrdata}; // write data has to be clocked at c0 in dram.v
assign dram_bsel = 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;
assign dram_addr = {22{next_loader}} & { 1'b1, 6'b000000, loader_addr[15:1] }
| {22{next_cpu}} & { cpu_csrom, {6{~cpu_csrom}} & cpu_addr[20:15], cpu_addr[14:0] }
| {22{next_vid}} & { 1'b0, video_addr }
| {22{next_ts }} & { 1'b0, ts_addr }
| {22{next_tm }} & { 1'b0, tm_addr }
| {22{next_dma}} & { 1'b0, dma_addr };
reg cpu_rnw_r;
always @(posedge clk) if (c3) cpu_rnw_r <= cpu_rnw;
// generation of read strobes: for video and cpu
always @(posedge clk) begin
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;
end
assign video_pre_next = curr_vid & c1;
assign video_next = curr_vid & c2;
assign video_strobe = curr_vid && c3;
assign video_next_strobe = next_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

BIN
rtl/periph/CMOS.bin Normal file

Binary file not shown.

21
rtl/periph/CMOS.mif Normal file
View File

@ -0,0 +1,21 @@
-- http://srecord.sourceforge.net/
--
-- Generated automatically by srec_cat -o --mif
--
DEPTH = 256;
WIDTH = 8;
ADDRESS_RADIX = HEX;
DATA_RADIX = HEX;
CONTENT BEGIN
0000: 00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 AA 00 00 00 00 00 00;
0018: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
0048: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
0078: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
00A8: 00 00 00 00 00 00 00 00 00 00 00 01 03 00 00 02 01 00 00 00 02 00 00 00;
00C0: 00 00 00 00 00 00 00 00 42 08 84 10 C6 18 08 21 4A 29 8C 31 CE 39 21 04;
00D8: 63 0C A5 14 E7 1C 29 25 6B 2D AD 35 EF 3D 78 7B 00 00 00 00 00 00 00 00;
00F0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00;
END;

View File

@ -1,66 +1,66 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// //
// PS2-to-Kempston Mouse v2 // PS2-to-Kempston Mouse v2
// (C) 2017,2018 Sorgelig // (C) 2017,2018 Sorgelig
// //
// This program is free software; you can redistribute it and/or modify it // This program 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 // under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option) // Software Foundation; either version 2 of the License, or (at your option)
// any later version. // any later version.
// //
// This program is distributed in the hope that it will be useful, but WITHOUT // This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details. // more details.
// //
// You should have received a copy of the GNU General Public License along // You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc., // with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
module kempston_mouse module kempston_mouse
( (
input clk_sys, input clk_sys,
input reset, input reset,
input [24:0] ps2_mouse, input [24:0] ps2_mouse,
input [2:0] addr, input [2:0] addr,
output sel, output sel,
output [7:0] dout output [7:0] dout
); );
assign dout = data; assign dout = data;
assign sel = port_sel; assign sel = port_sel;
reg [11:0] dx; reg [11:0] dx;
reg [11:0] dy; reg [11:0] dy;
reg [7:0] data; reg [7:0] data;
reg port_sel; reg port_sel;
always @* begin always @* begin
port_sel = 1; port_sel = 1;
casex(addr) casex(addr)
3'b011: data = dx[7:0]; 3'b011: data = dx[7:0];
3'b111: data = dy[7:0]; 3'b111: data = dy[7:0];
3'bX10: data = ~{5'b00000,ps2_mouse[2], ps2_mouse[0], ps2_mouse[1]} ; 3'bX10: data = ~{5'b00000,ps2_mouse[2], ps2_mouse[0], ps2_mouse[1]} ;
default: {port_sel,data} = 8'hFF; default: {port_sel,data} = 8'hFF;
endcase endcase
end end
always @(posedge clk_sys) begin always @(posedge clk_sys) begin
reg old_status; reg old_status;
old_status <= ps2_mouse[24]; old_status <= ps2_mouse[24];
if(reset) begin if(reset) begin
dx <= 128; // dx != dy for better mouse detection dx <= 128; // dx != dy for better mouse detection
dy <= 0; dy <= 0;
end end
else if(old_status != ps2_mouse[24]) begin else if(old_status != ps2_mouse[24]) begin
dx <= dx + {{4{ps2_mouse[4]}},ps2_mouse[15:8]}; dx <= dx + {{4{ps2_mouse[4]}},ps2_mouse[15:8]};
dy <= dy + {{4{ps2_mouse[5]}},ps2_mouse[23:16]}; dy <= dy + {{4{ps2_mouse[5]}},ps2_mouse[23:16]};
end end
end end
endmodule endmodule

View File

@ -59,17 +59,17 @@ always @(*) begin
8'h0c : Dout <= c_reg; 8'h0c : Dout <= c_reg;
8'h0d : Dout <= 8'b10000000; 8'h0d : Dout <= 8'b10000000;
8'hb1 : Dout <= CMOSCfg[7:6]; // CPU Speed // 8'hb1 : Dout <= CMOSCfg[7:6]; // CPU Speed
8'hb2 : Dout <= 0; // Boot device // 8'hb2 : Dout <= 0; // Boot device
8'hb3 : Dout <= CMOSCfg[8]; // CPU Cache // 8'hb3 : Dout <= CMOSCfg[8]; // CPU Cache
8'hb4 : Dout <= CMOSCfg[13:11]; // F11 // 8'hb4 : Dout <= CMOSCfg[13:11]; // F11
8'hb5 : Dout <= CMOSCfg[15:14]; // F11 bank // 8'hb5 : Dout <= CMOSCfg[15:14]; // F11 bank
8'hb6 : Dout <= CMOSCfg[18:16]; // Shift+F11 // 8'hb6 : Dout <= CMOSCfg[18:16]; // Shift+F11
8'hb7 : Dout <= CMOSCfg[20:19]; // Shift+F11 bank // 8'hb7 : Dout <= CMOSCfg[20:19]; // Shift+F11 bank
8'hb8 : Dout <= CMOSCfg[10:9]; // #7FFD // 8'hb8 : Dout <= CMOSCfg[10:9]; // #7FFD
8'hb9 : Dout <= CMOSCfg[23:21]; // ZX Palette // 8'hb9 : Dout <= CMOSCfg[23:21]; // ZX Palette
8'hba : Dout <= CMOSCfg[24]; // NGS Reset // 8'hba : Dout <= CMOSCfg[24]; // NGS Reset
8'hbb : Dout <= CMOSCfg[27:25]; // INT offset // 8'hbb : Dout <= CMOSCfg[27:25]; // INT offset
8'hf0 : Dout <= KEYSCANCODE; 8'hf0 : Dout <= KEYSCANCODE;
default: Dout <= CMOS_Dout; default: Dout <= CMOS_Dout;
@ -233,7 +233,7 @@ always @(posedge CLK) begin
end end
// 50 Bytes of General Purpose RAM // 50 Bytes of General Purpose RAM
dpram #(.DATAWIDTH(8), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/rtc/CMOS.mif")) CMOS dpram #(.DATAWIDTH(8), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/periph/CMOS.mif")) CMOS
( (
.clock (CLK), .clock (CLK),
.address_a (A), .address_a (A),

60
rtl/periph/vdac.v Normal file
View File

@ -0,0 +1,60 @@
module vdac
(
input wire mode,
input wire [4:0] o_r, // input from FPGA
input wire [4:0] o_g,
input wire [4:0] o_b,
output wire [7:0] v_r, // output to VDAC
output wire [7:0] v_g,
output wire [7:0] v_b
);
vdac_lut vdac_lut_r (.mode(mode), .in(o_r), .out(v_r));
vdac_lut vdac_lut_g (.mode(mode), .in(o_g), .out(v_g));
vdac_lut vdac_lut_b (.mode(mode), .in(o_b), .out(v_b));
endmodule
module vdac_lut
(
input wire mode,
input wire [4:0] in,
output wire [7:0] out
);
reg [7:0] lut;
assign out = mode ? {in, 3'b0} : lut;
always @*
case (in)
5'd0: lut = 8'd0;
5'd1: lut = 8'd10;
5'd2: lut = 8'd21;
5'd3: lut = 8'd31;
5'd4: lut = 8'd42;
5'd5: lut = 8'd53;
5'd6: lut = 8'd63;
5'd7: lut = 8'd74;
5'd8: lut = 8'd85;
5'd9: lut = 8'd95;
5'd10: lut = 8'd106;
5'd11: lut = 8'd117;
5'd12: lut = 8'd127;
5'd13: lut = 8'd138;
5'd14: lut = 8'd149;
5'd15: lut = 8'd159;
5'd16: lut = 8'd170;
5'd17: lut = 8'd181;
5'd18: lut = 8'd191;
5'd19: lut = 8'd202;
5'd20: lut = 8'd213;
5'd21: lut = 8'd223;
5'd22: lut = 8'd234;
5'd23: lut = 8'd245;
5'd24: lut = 8'd255;
default: lut = 8'd255;
endcase
endmodule

Binary file not shown.

View File

@ -1,71 +0,0 @@
-- Copyright (C) 2017 Intel Corporation. All rights reserved.
-- Your use of Intel Corporation's design tools, logic functions
-- and other software and tools, and its AMPP partner logic
-- functions, and any output files from any of the foregoing
-- (including device programming or simulation files), and any
-- associated documentation or information are expressly subject
-- to the terms and conditions of the Intel Program License
-- Subscription Agreement, the Intel Quartus Prime License Agreement,
-- the Intel MegaCore Function License Agreement, or other
-- applicable license agreement, including, without limitation,
-- that your use is for the sole purpose of programming logic
-- devices manufactured by Intel and sold by Intel or its
-- authorized distributors. Please refer to the applicable
-- agreement for further details.
-- Quartus Prime generated Memory Initialization File (.mif)
WIDTH=8;
DEPTH=256;
ADDRESS_RADIX=HEX;
DATA_RADIX=HEX;
CONTENT BEGIN
[000..010] : 00;
011 : AA;
[012..0B0] : 00;
0B1 : 01;
0B2 : 00;
0B3 : 01;
0B4 : 03;
[0B5..0B7] : 00;
0B8 : 01;
[0B9..0BA] : 00;
0BB : 02;
[0BC..0CD] : FF;
[0CE..0CF] : 00;
0D0 : 42;
0D1 : 08;
0D2 : 84;
0D3 : 10;
0D4 : C6;
0D5 : 18;
0D6 : 08;
0D7 : 21;
0D8 : 4A;
0D9 : 29;
0DA : 8C;
0DB : 31;
0DC : CE;
0DD : 39;
0DE : 21;
0DF : 04;
0E0 : 63;
0E1 : 0C;
0E2 : A5;
0E3 : 14;
0E4 : E7;
0E5 : 1C;
0E6 : 29;
0E7 : 25;
0E8 : 6B;
0E9 : 2D;
0EA : AD;
0EB : 35;
0EC : EF;
0ED : 3D;
0EE : 6B;
0EF : A2;
[0F0..0FF] : 00;
END;

View File

@ -1,88 +0,0 @@
// part of NeoGS project (c) 2007-2008 NedoPC
//
// SPI mode 0 8-bit master module
//
// short diagram for speed=0 (Fclk/Fspi=2, no rdy shown)
//
// clk: ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ (positive edges)
// counter: 00|00|00|10|11|12|13|14|15|16|17|18|19|1A|1B|1C|1D|1E|1F|00|00|00 // internal!
// sck: ___________/``\__/``\__/``\__/``\__/``\__/``\__/``\__/``\_______
// sdo: --------< do7 | do6 | do5 | do4 | do3 | do2 | do1 | do0 >-------
// sdi: --------< di7 | di6 | di5 | di4 | di3 | di2 | di1 | di0 >-------
// bsync: ________/`````\_________________________________________________
// start: _____/``\_______________________________________________________
// din: -----<IN>-------------------------------------------------------
// dout: old old old old old old old old old old old old old | new new new
//
// data on sdo must be latched by slave on rising sck edge. data on sdo changes on falling edge of sck
//
// data from sdi is latched by master on positive edge of sck, while slave changes it on falling edge.
// WARNING: slave must emit valid di7 bit BEFORE first pulse on sck!
//
// start is synchronous pulse, which starts all transfer and also latches din data on the same clk edge
// as it is registered high. start can be given anytime (only when speed=0),
// so it is functioning then as synchronous reset. when speed!=0, there is global enable for majority of
// flipflops in the module, so start can't be accepted at any time
//
// dout updates with freshly received data at the clk edge in which sck goes high for the last time, thus
// latching last bit on sdi.
//
// sdo emits last bit shifted out after the transfer end
module spi
(
// SPI wires
input clk, // system clock
output sck, // SCK
output reg sdo, // MOSI
input sdi, // MISO
// DMA interface
input dma_req,
input [7:0] dma_din,
// Z80 interface
input cpu_req,
input [7:0] cpu_din,
// output
output start, // start strobe, 1 clock length
output reg [7:0] dout
);
assign sck = counter[0];
assign start = req && rdy;
wire [7:0] din = dma_req ? dma_din : cpu_din;
wire rdy = counter[4]; // 0 - transmission in progress
wire req = cpu_req || dma_req;
reg [4:0] counter;
always @(posedge clk) begin
reg [7:0] shift;
if (start) begin
counter <= 5'b0;
sdo <= din[7];
shift[7:1] <= din[6:0];
end
else if (!rdy) begin
counter <= counter + 5'd1;
// shift in (rising edge of SCK)
if (!sck) begin
shift[0] <= sdi;
if (&counter[3:1]) dout <= {shift[7:1], sdi};
end
// shift out (falling edge of sck)
if (sck) begin
sdo <= shift[7];
shift[7:1] <= shift[6:0]; // last bit remains after end of exchange
end
end
end
endmodule

Binary file not shown.

File diff suppressed because it is too large Load Diff

28
rtl/tune.v Normal file
View File

@ -0,0 +1,28 @@
`timescale 1ns/100ps
`ifdef MODEL_TECH
`define SIMULATE
`endif
//`define DRAMMEM_VERBOSE
//`define FETCH_VERBOSE
// `define FREE_IORQ // for non-blocked by internal ports !IORQ
// `define IDE_HDD // for IDE HDD
`define IDE_VDAC // for VideoDAC instead of IDE
// `define IDE_VDAC2 // for VideoDAC2 instead of IDE
`define XTR_FEAT // extra features, in only IDEless version
// `define SD_CARD2 // for second SD Card
// `define AUTO_INT // auto-incremented Frame Interrpt
// `define FDR // FDD Ripper version (use with DISABLE_TSU)
// `define DISABLE_TSU // disable TSU
// `define PENT_312 // for Pentagon 71680 tacts emulation with 312 video lines
`define KEMPSTON_8BIT // 8-bit enhanced Kempston Joystick interface

View File

@ -1,33 +1,35 @@
// This module fetches video data from DRAM // This module fetches video data from DRAM
`include "tune.v"
module video_fetch module video_fetch
( (
// clocks // clocks
input wire clk, input wire clk,
// control // control
input wire [3:0] f_sel, input wire [3:0] f_sel,
input wire [1:0] b_sel, input wire [1:0] b_sel,
input wire fetch_stb, input wire fetch_stb,
// video data // video data
output reg [31:0] fetch_data, output reg [31:0] fetch_data,
output reg [31:0] fetch_temp, output reg [31:0] fetch_temp,
// DRAM interface // DRAM interface
input wire video_strobe, input wire video_strobe,
input wire [15:0] video_data input wire [15:0] video_data
); );
// fetching data always @(posedge clk) if (video_strobe)
always @(posedge clk) if (video_strobe) begin
begin if (f_sel[0]) fetch_temp[ 7: 0] <= b_sel[0] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[0]) fetch_temp[ 7: 0] <= b_sel[0] ? video_data[15:8] : video_data[ 7:0]; if (f_sel[1]) fetch_temp[15: 8] <= b_sel[1] ? video_data[15:8] : video_data[ 7:0];
if (f_sel[1]) fetch_temp[15: 8] <= b_sel[1] ? video_data[15:8] : video_data[ 7:0]; if (f_sel[2]) fetch_temp[23:16] <= video_data[ 7:0];
if (f_sel[2]) fetch_temp[23:16] <= video_data[ 7:0]; if (f_sel[3]) fetch_temp[31:24] <= video_data[15:8];
if (f_sel[3]) fetch_temp[31:24] <= video_data[15:8]; end
end
always @(posedge clk) if (fetch_stb)
always @(posedge clk) if (fetch_stb) fetch_data <= fetch_temp; fetch_data <= fetch_temp;
endmodule endmodule

View File

@ -1,235 +1,230 @@
// This module decodes video modes
// This module decodes video modes
`include "tune.v"
module video_mode module video_mode
( (
// clocks // clocks
input wire clk, f1, c3, input wire clk, f1, c3,
// video config
input wire [7:0] vpage,
input wire [7:0] vconf,
input wire ts_rres_ext,
// video parameters & mode controls
input wire [8:0] gx_offs,
output wire [9:0] x_offs_mode,
output wire [8:0] hpix_beg,
output wire [8:0] hpix_end,
output wire [8:0] vpix_beg,
output wire [8:0] vpix_end,
output wire [8:0] hpix_beg_ts,
output wire [8:0] hpix_end_ts,
output wire [8:0] vpix_beg_ts,
output wire [8:0] vpix_end_ts,
output wire [5:0] x_tiles,
output wire [4:0] go_offs,
output wire [3:0] fetch_sel,
output wire [1:0] fetch_bsl,
input wire [3:0] fetch_cnt,
input wire pix_start,
input wire line_start_s,
output wire tv_hires,
output wire [1:0] render_mode,
output wire pix_stb,
output wire fetch_stb,
// video data
input wire [15:0] txt_char,
// video counters
input wire [7:0] cnt_col,
input wire [8:0] cnt_row,
input wire cptr,
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw
);
wire [1:0] vmod = vconf[1:0];
wire [1:0] rres = vconf[7:6];
// clocking strobe for pixels (TV)
assign pix_stb = tv_hires ? f1 : c3;
// Modes
localparam M_ZX = 2'h0; // ZX
localparam M_HC = 2'h1; // 16c
localparam M_XC = 2'h2; // 256c
localparam M_TX = 2'h3; // Text
// Render modes (affects 'video_render.v')
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// fetch strobes
wire ftch[0:3];
assign fetch_stb = (pix_start | ftch[render_mode]) && c3;
assign ftch[R_ZX] = &fetch_cnt[3:0];
assign ftch[R_HC] = &fetch_cnt[1:0];
assign ftch[R_XC] = fetch_cnt[0];
assign ftch[R_TX] = &fetch_cnt[3:0];
// fetch window
wire [4:0] g_offs[0:3];
// these values are from a thin air!!! recheck them occasionally!
assign g_offs[M_ZX] = 5'd18;
assign g_offs[M_HC] = 5'd6;
assign g_offs[M_XC] = 5'd4;
assign g_offs[M_TX] = 5'd10;
assign go_offs = g_offs[vmod];
// fetch selectors
// Attention: counter is already incremented at the time of video data fetching!
// wire m_c = (vmod == M_HC) | (vmod == M_XC);
// assign fetch_sel = vmod == M_TX ? f_txt_sel[cnt_col[1:0]] : {~cptr, ~cptr, cptr | m_c, cptr | m_c};
// wire [3:0] f_sel[0:7];
wire [3:0] f_sel[0:3];
assign f_sel[M_ZX] = {~cptr, ~cptr, cptr, cptr};
assign f_sel[M_HC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_XC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_TX] = f_txt_sel[cnt_col[1:0]];
assign fetch_sel = f_sel[vmod];
assign fetch_bsl = (vmod == M_TX) ? f_txt_bsl[cnt_col[1:0]] : 2'b10;
// wire [1:0] f_bsl[0:7];
// assign f_bsl[M_ZX] = 2'b10;
// assign f_bsl[M_HC] = 2'b10;
// assign f_bsl[M_XC] = 2'b10;
// assign f_bsl[M_TX] = f_txt_bsl[cnt_col[1:0]];
// assign fetch_bsl = f_bsl[vmod];
wire [3:0] f_txt_sel[0:3];
assign f_txt_sel[1] = 4'b0011; // char
assign f_txt_sel[2] = 4'b1100; // attr
assign f_txt_sel[3] = 4'b0001; // gfx0
assign f_txt_sel[0] = 4'b0010; // gfx1
wire [1:0] f_txt_bsl[0:3];
assign f_txt_bsl[1] = 2'b10; // char
assign f_txt_bsl[2] = 2'b10; // attr
assign f_txt_bsl[3] = {2{cnt_row[0]}}; // gfx0
assign f_txt_bsl[0] = {2{cnt_row[0]}}; // gfx1
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
// [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
// [2:0] - need cycles
wire [4:0] bw[0:3];
assign bw[M_ZX] = {BW8, BU1}; // '1 of 8' (ZX)
assign bw[M_HC] = {BW4, BU1}; // '1 of 4' (16c)
assign bw[M_XC] = {BW2, BU1}; // '1 of 2' (256c)
assign bw[M_TX] = {BW8, BU4}; // '4 of 8' (text)
assign video_bw = bw[vmod];
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
// render mode
// wire [1:0] r_mode[0:7];
wire [1:0] r_mode[0:3];
assign r_mode[M_ZX] = R_ZX;
assign r_mode[M_HC] = R_HC;
assign r_mode[M_XC] = R_XC;
assign r_mode[M_TX] = R_TX;
assign render_mode = r_mode[vmod];
// raster resolution
wire [8:0] hp_beg[0:3];
wire [8:0] hp_end[0:3];
wire [8:0] vp_beg[0:3];
wire [8:0] vp_end[0:3];
wire [5:0] x_tile[0:3];
assign hp_beg[0] = 9'd136; // 256 (88-52-256-52)
assign hp_beg[1] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[2] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[3] = 9'd88; // 360 (88-0-360-0)
assign hp_end[0] = 9'd392; // 256
assign hp_end[1] = 9'd428; // 320
assign hp_end[2] = 9'd428; // 320
assign hp_end[3] = 9'd448; // 360
assign vp_beg[0] = 9'd080; // 192 (22-24-192-24)/(32-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = 9'd076; // 200 (22-20-200-20)/(32-44-200-44)
assign vp_beg[2] = 9'd056; // 240 (22-0-240-0)/(32-24-240-24)
assign vp_beg[3] = 9'd032; // 288 (22-0-240-0)/(32-0-288-0)
assign vp_end[0] = 9'd272; // 192
assign vp_end[1] = 9'd276; // 200
assign vp_end[2] = 9'd296; // 240
assign vp_end[3] = 9'd320; // 240/288
assign x_tile[0] = 6'd34; // 256
assign x_tile[1] = 6'd42; // 320
assign x_tile[2] = 6'd42; // 320
assign x_tile[3] = 6'd47; // 360
assign hpix_beg = hp_beg[rres];
assign hpix_end = hp_end[rres];
assign vpix_beg = vp_beg[rres];
assign vpix_end = vp_end[rres];
assign hpix_beg_ts = ts_rres_ext ? hp_beg[3] : hp_beg[rres]; // video config
assign hpix_end_ts = ts_rres_ext ? hp_end[3] : hp_end[rres]; input wire [7:0] vpage,
assign vpix_beg_ts = ts_rres_ext ? vp_beg[3] : vp_beg[rres]; input wire [7:0] vconf,
assign vpix_end_ts = ts_rres_ext ? vp_end[3] : vp_end[rres]; input wire ts_rres_ext,
input wire v60hz,
assign x_tiles = ts_rres_ext ? x_tile[3] : x_tile[rres]; // video parameters & mode controls
input wire [8:0] gx_offs,
// videomode addresses output wire [9:0] x_offs_mode,
wire [20:0] v_addr[0:3]; output wire [8:0] hpix_beg,
assign v_addr[M_ZX] = addr_zx; output wire [8:0] hpix_end,
assign v_addr[M_HC] = addr_16c; output wire [8:0] vpix_beg,
assign v_addr[M_XC] = addr_256c; output wire [8:0] vpix_end,
assign v_addr[M_TX] = addr_text; output wire [8:0] hpix_beg_ts,
assign video_addr = v_addr[vmod]; output wire [8:0] hpix_end_ts,
output wire [8:0] vpix_beg_ts,
// ZX output wire [8:0] vpix_end_ts,
wire [20:0] addr_zx = {vpage, 1'b0, ~cnt_col[0] ? addr_zx_gfx : addr_zx_atr}; output wire [5:0] x_tiles,
wire [11:0] addr_zx_gfx = {cnt_row[7:6], cnt_row[2:0], cnt_row[5:3], cnt_col[4:1]}; output wire [4:0] go_offs,
wire [11:0] addr_zx_atr = {3'b110, cnt_row[7:3], cnt_col[4:1]}; output wire [3:0] fetch_sel,
output wire [1:0] fetch_bsl,
// 16c input wire [3:0] fetch_cnt,
wire [20:0] addr_16c = {vpage[7:3], cnt_row, cnt_col[6:0]}; input wire pix_start,
input wire line_start_s,
// 256c output wire tv_hires,
wire [20:0] addr_256c = {vpage[7:4], cnt_row, cnt_col[7:0]}; output reg vga_hires = 0,
output wire [1:0] render_mode,
// Textmode output wire pix_stb,
wire [20:0] addr_text = {vpage[7:1], addr_tx[cnt_col[1:0]]}; output wire fetch_stb,
wire [13:0] addr_tx[0:3];
assign addr_tx[0] = {vpage[0], cnt_row[8:3], 1'b0, cnt_col[7:2]}; // char codes, data[15:0] // video data
assign addr_tx[1] = {vpage[0], cnt_row[8:3], 1'b1, cnt_col[7:2]}; // char attributes, data[31:16] input wire [15:0] txt_char,
assign addr_tx[2] = {~vpage[0], 3'b000, (txt_char[7:0]), cnt_row[2:1]}; // char0 graphics, data[7:0]
assign addr_tx[3] = {~vpage[0], 3'b000, (txt_char[15:8]), cnt_row[2:1]}; // char1 graphics, data[15:8] // video counters
input wire [7:0] cnt_col,
input wire [8:0] cnt_row,
endmodule input wire cptr,
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw
);
wire [1:0] vmod = vconf[1:0];
wire [1:0] rres = vconf[7:6];
// clocking strobe for pixels (TV)
assign pix_stb = tv_hires ? f1 : c3;
always @(posedge clk)
if (line_start_s)
vga_hires <= tv_hires;
// Modes
localparam M_ZX = 2'h0; // ZX
localparam M_HC = 2'h1; // 16c
localparam M_XC = 2'h2; // 256c
localparam M_TX = 2'h3; // Text
// Render modes (affects 'video_render.v')
localparam R_ZX = 2'h0;
localparam R_HC = 2'h1;
localparam R_XC = 2'h2;
localparam R_TX = 2'h3;
// fetch strobes
wire ftch[0:3];
assign fetch_stb = (pix_start | ftch[render_mode]) && c3;
assign ftch[R_ZX] = &fetch_cnt[3:0];
assign ftch[R_HC] = &fetch_cnt[1:0];
assign ftch[R_XC] = fetch_cnt[0];
assign ftch[R_TX] = &fetch_cnt[3:0];
// fetch window
wire [4:0] g_offs[0:3];
assign g_offs[M_ZX] = 5'd18;
assign g_offs[M_HC] = 5'd6;
assign g_offs[M_XC] = 5'd4;
assign g_offs[M_TX] = 5'd10;
assign go_offs = g_offs[vmod];
// fetch selectors
// Attention: counter is already incremented at the time of video data fetching!
wire [3:0] f_sel[0:3];
wire [3:0] f_txt_sel[0:3];
wire [1:0] f_txt_bsl[0:3];
assign f_sel[M_ZX] = {~cptr, ~cptr, cptr, cptr};
assign f_sel[M_HC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_XC] = {~cptr, ~cptr, 2'b11};
assign f_sel[M_TX] = f_txt_sel[cnt_col[1:0]];
assign fetch_sel = f_sel[vmod];
assign fetch_bsl = (vmod == M_TX) ? f_txt_bsl[cnt_col[1:0]] : 2'b10;
assign f_txt_sel[1] = 4'b0011; // char
assign f_txt_sel[2] = 4'b1100; // attr
assign f_txt_sel[3] = 4'b0001; // gfx0
assign f_txt_sel[0] = 4'b0010; // gfx1
assign f_txt_bsl[1] = 2'b10; // char
assign f_txt_bsl[2] = 2'b10; // attr
assign f_txt_bsl[3] = {2{cnt_row[0]}}; // gfx0
assign f_txt_bsl[0] = {2{cnt_row[0]}}; // gfx1
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
// [4:3] - total cycles: 11 = 8 / 01 = 4 / 00 = 2
// [2:0] - need cycles
wire [4:0] bw[0:3];
assign bw[M_ZX] = {BW8, BU1}; // '1 of 8' (ZX)
assign bw[M_HC] = {BW4, BU1}; // '1 of 4' (16c)
assign bw[M_XC] = {BW2, BU1}; // '1 of 2' (256c)
assign bw[M_TX] = {BW8, BU4}; // '4 of 8' (text)
assign video_bw = bw[vmod];
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
// render mode
wire [1:0] r_mode[0:3];
assign r_mode[M_ZX] = R_ZX;
assign r_mode[M_HC] = R_HC;
assign r_mode[M_XC] = R_XC;
assign r_mode[M_TX] = R_TX;
assign render_mode = r_mode[vmod];
// raster resolution
wire [8:0] hp_beg[0:3];
wire [8:0] hp_end[0:3];
wire [8:0] vp_beg[0:3];
wire [8:0] vp_end[0:3];
wire [5:0] x_tile[0:3];
assign hp_beg[0] = 9'd140; // 256 (88-52-256-52)
assign hp_beg[1] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[2] = 9'd108; // 320 (88-20-320-20)
assign hp_beg[3] = 9'd88; // 360 (88-0-360-0)
assign hp_end[0] = 9'd396; // 256
assign hp_end[1] = 9'd428; // 320
assign hp_end[2] = 9'd428; // 320
assign hp_end[3] = 9'd448; // 360
`ifdef PENT_312
assign vp_beg[0] = v60hz ? 9'd046 : 9'd072; // 192 (22-24-192-24)/(24-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = v60hz ? 9'd042 : 9'd068; // 200 (22-20-200-20)/(24-44-200-44)
assign vp_beg[2] = v60hz ? 9'd022 : 9'd048; // 240 (22-0-240-0)/(24-24-240-24)
assign vp_beg[3] = v60hz ? 9'd022 : 9'd024; // 240/288 (22-0-240-0)/(24-0-288-0)
assign vp_end[0] = v60hz ? 9'd238 : 9'd264; // 192
assign vp_end[1] = v60hz ? 9'd242 : 9'd268; // 200
assign vp_end[2] = v60hz ? 9'd262 : 9'd288; // 240
assign vp_end[3] = v60hz ? 9'd262 : 9'd312; // 240/288
`else
assign vp_beg[0] = v60hz ? 9'd046 : 9'd080; // 192 (22-24-192-24)/(32-48-192-48) (blank-border-pixels-border)
assign vp_beg[1] = v60hz ? 9'd042 : 9'd076; // 200 (22-20-200-20)/(32-44-200-44)
assign vp_beg[2] = v60hz ? 9'd022 : 9'd056; // 240 (22-0-240-0)/(32-24-240-24)
assign vp_beg[3] = v60hz ? 9'd022 : 9'd032; // 240/288 (22-0-240-0)/(32-0-288-0)
assign vp_end[0] = v60hz ? 9'd238 : 9'd272; // 192
assign vp_end[1] = v60hz ? 9'd242 : 9'd276; // 200
assign vp_end[2] = v60hz ? 9'd262 : 9'd296; // 240
assign vp_end[3] = v60hz ? 9'd262 : 9'd320; // 240/288
`endif
assign x_tile[0] = 6'd34; // 256
assign x_tile[1] = 6'd42; // 320
assign x_tile[2] = 6'd42; // 320
assign x_tile[3] = 6'd47; // 360
assign hpix_beg = hp_beg[rres];
assign hpix_end = hp_end[rres];
assign vpix_beg = vp_beg[rres];
assign vpix_end = vp_end[rres];
assign hpix_beg_ts = ts_rres_ext ? hp_beg[3] : hp_beg[rres];
assign hpix_end_ts = ts_rres_ext ? hp_end[3] : hp_end[rres];
assign vpix_beg_ts = ts_rres_ext ? vp_beg[3] : vp_beg[rres];
assign vpix_end_ts = ts_rres_ext ? vp_end[3] : vp_end[rres];
assign x_tiles = ts_rres_ext ? x_tile[3] : x_tile[rres];
// ZX
wire [11:0] addr_zx_gfx = {cnt_row[7:6], cnt_row[2:0], cnt_row[5:3], cnt_col[4:1]};
wire [11:0] addr_zx_atr = {3'b110, cnt_row[7:3], cnt_col[4:1]};
wire [20:0] addr_zx = {vpage, 1'b0, ~cnt_col[0] ? addr_zx_gfx : addr_zx_atr};
// 16c
wire [20:0] addr_16c = {vpage[7:3], cnt_row, cnt_col[6:0]};
// 256c
wire [20:0] addr_256c = {vpage[7:4], cnt_row, cnt_col[7:0]};
// Textmode
wire [13:0] addr_tx[0:3];
wire [20:0] addr_text = {vpage[7:1], addr_tx[cnt_col[1:0]]};
assign addr_tx[0] = {vpage[0], cnt_row[8:3], 1'b0, cnt_col[7:2]}; // char codes, data[15:0]
assign addr_tx[1] = {vpage[0], cnt_row[8:3], 1'b1, cnt_col[7:2]}; // char attributes, data[31:16]
assign addr_tx[2] = {~vpage[0], 3'b000, (txt_char[7:0]), cnt_row[2:1]}; // char0 graphics, data[7:0]
assign addr_tx[3] = {~vpage[0], 3'b000, (txt_char[15:8]), cnt_row[2:1]}; // char1 graphics, data[15:8]
// videomode addresses
wire [20:0] v_addr[0:3];
assign v_addr[M_ZX] = addr_zx;
assign v_addr[M_HC] = addr_16c;
assign v_addr[M_XC] = addr_256c;
assign v_addr[M_TX] = addr_text;
assign video_addr = v_addr[vmod];
endmodule

View File

@ -2,59 +2,181 @@
// This module generates video for DAC // This module generates video for DAC
// (c)2015 TSL // (c)2015 TSL
`include "tune.v"
module video_out module video_out
( (
// clocks // clocks
input wire clk, c3, input wire clk, c3,
// video controls // video controls
input wire tv_blank, input wire vga_on,
input wire [1:0] plex_sel_in, input wire tv_blank,
input wire vga_blank,
input wire vga_line,
input wire [1:0] plex_sel_in,
// mode controls // mode controls
input wire tv_hires, input wire tv_hires,
input wire [3:0] palsel, input wire vga_hires,
input wire [3:0] palsel,
// Z80 pins // Z80 pins
input wire [15:0] cram_data_in, input wire [15:0] cram_data_in,
input wire [7:0] cram_addr_in, input wire [7:0] cram_addr_in,
input wire cram_we, input wire cram_we,
// video data // video data
input wire [7:0] vplex_in, input wire [7:0] vplex_in,
output wire [7:0] vred, input wire [7:0] vgaplex,
output wire [7:0] vgrn, output wire [1:0] vred,
output wire [7:0] vblu, output wire [1:0] vgrn,
output wire vdac_mode output wire [1:0] vblu,
output wire [4:0] vred_raw,
output wire [4:0] vgrn_raw,
output wire [4:0] vblu_raw,
output wire vdac_mode
); );
wire [14:0] vpix;
wire [15:0] vpixel;
wire [1:0] phase;
wire [7:0] pwm[0:7];
reg [7:0] vplex; reg blank1; // GOVNOKOD!!!!!!!!!!!!!!!!!!!!!
always @(posedge clk) if (c3) vplex <= vplex_in;
wire [7:0] vdata = tv_hires ? {palsel, plex_sel_in[1] ? vplex[3:0] : vplex[7:4]} : vplex; assign vred_raw = vpix[14:10];
assign vgrn_raw = vpix[9:5];
assign vblu_raw = vpix[4:0];
assign vdac_mode = vpixel[15];
// TV/VGA mux
reg [7:0] vplex;
always @(posedge clk) if (c3)
vplex <= vplex_in;
wire [7:0] plex = vga_on ? vgaplex : vplex;
wire hires = vga_on ? vga_hires : tv_hires;
wire plex_sel = vga_on ? plex_sel_in[0] : plex_sel_in[1];
wire [7:0] vdata = hires ? {palsel, plex_sel ? plex[3:0] : plex[7:4]} : plex;
wire blank = vga_on ? vga_blank : tv_blank;
assign vpix = blank1 ? 15'b0 : vpixel[14:0];
// assign vpix = blank1 ? 15'b0 : (vpixel[14:0] & 15'b111001110011100); // test for 373 colors
// assign vpix = blank1 ? 15'b0 : (vpixel[14:0] & 15'b110001100011000); // test for 64 colors
// GOVNOKOD!!!!!!!!!!!!!!!!!!!!!
always @(posedge clk)
begin
blank1 <= blank;
end
// color components extraction
wire [1:0] cred = vpix[14:13];
wire [2:0] ired = vpix[12:10];
wire [1:0] cgrn = vpix[ 9: 8];
wire [2:0] igrn = vpix[ 7: 5];
wire [1:0] cblu = vpix[ 4: 3];
wire [2:0] iblu = vpix[ 2: 0];
// prepare and clocking two phases of output
reg [1:0] red0;
reg [1:0] grn0;
reg [1:0] blu0;
reg [1:0] red1;
reg [1:0] grn1;
reg [1:0] blu1;
always @(posedge clk)
begin
red0 <= (!pwm[ired][{phase, 1'b0}] | &cred) ? cred : (cred + 2'b1);
grn0 <= (!pwm[igrn][{phase, 1'b0}] | &cgrn) ? cgrn : (cgrn + 2'b1);
blu0 <= (!pwm[iblu][{phase, 1'b0}] | &cblu) ? cblu : (cblu + 2'b1);
red1 <= (!pwm[ired][{phase, 1'b1}] | &cred) ? cred : (cred + 2'b1);
grn1 <= (!pwm[igrn][{phase, 1'b1}] | &cgrn) ? cgrn : (cgrn + 2'b1);
blu1 <= (!pwm[iblu][{phase, 1'b1}] | &cblu) ? cblu : (cblu + 2'b1);
end
`ifdef IDE_VDAC
// no PWM
assign vred = cred;
assign vgrn = cgrn;
assign vblu = cblu;
`elsif IDE_VDAC2
// no PWM
assign vred = cred;
assign vgrn = cgrn;
assign vblu = cblu;
`else
// output muxing for 56MHz PWM resolution
assign vred = clk ? red1 : red0;
assign vgrn = clk ? grn1 : grn0;
assign vblu = clk ? blu1 : blu0;
`endif
// PWM phase
reg [1:0] ph;
always @(posedge clk)
ph <= ph + 2'b1;
assign phase = {vga_on ? vga_line : ph[1], ph[0]};
// PWM
assign pwm[0] = 8'b00000000;
assign pwm[1] = 8'b00000001;
assign pwm[2] = 8'b01000001;
assign pwm[3] = 8'b01000101;
assign pwm[4] = 8'b10100101;
assign pwm[5] = 8'b10100111;
assign pwm[6] = 8'b11010111;
assign pwm[7] = 8'b11011111;
// CRAM // CRAM
wire [15:0] vpixel; dpram #(.DATAWIDTH(16), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/video/video_cram.mif")) video_cram
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8), .MEM_INIT_FILE("rtl/video/video_cram.mif")) video_cram (
( .clock (clk),
.clock (clk), .address_a(cram_addr_in),
.address_a(cram_addr_in), .data_a (cram_data_in),
.data_a (cram_data_in), .wren_a (cram_we),
.wren_a (cram_we), .address_b(vdata),
.address_b(vdata), .q_b (vpixel)
.q_b (vpixel) );
); /*
altdpram video_cram
reg blank; (
always @(posedge clk) blank <= tv_blank; .inclock (clk),
.data (cram_data_in),
wire [14:0] vpix = blank ? 15'b0 : vpixel[14:0]; .rdaddress (vdata),
.wraddress (cram_addr_in),
assign vred = {vpix[14:10], vpix[14:12]}; .wren (cram_we),
assign vgrn = {vpix[ 9: 5], vpix[ 9: 7]}; .q (vpixel),
assign vblu = {vpix[ 4: 0], vpix[ 4: 2]}; .aclr (1'b0),
assign vdac_mode = vpixel[15]; .byteena (1'b1),
.inclocken (1'b1),
.outclock (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_cram.indata_aclr = "OFF",
video_cram.indata_reg = "INCLOCK",
video_cram.intended_device_family = "ACEX1K",
video_cram.lpm_file = "../video/mem/video_cram.mif",
video_cram.lpm_type = "altdpram",
video_cram.outdata_aclr = "OFF",
video_cram.outdata_reg = "UNREGISTERED",
video_cram.rdaddress_aclr = "OFF",
video_cram.rdaddress_reg = "INCLOCK",
video_cram.rdcontrol_aclr = "OFF",
video_cram.rdcontrol_reg = "UNREGISTERED",
video_cram.width = 16,
video_cram.widthad = 8,
video_cram.wraddress_aclr = "OFF",
video_cram.wraddress_reg = "INCLOCK",
video_cram.wrcontrol_aclr = "OFF",
video_cram.wrcontrol_reg = "INCLOCK";
*/
endmodule endmodule

View File

@ -1,149 +1,166 @@
// This module latches all port parameters for video from Z80 // This module latches all port parameters for video from Z80
module video_ports
(
// clocks
input wire clk,
input wire [ 7:0] d,
input wire res,
input wire int_start,
input wire line_start_s,
// port write strobes
input wire zborder_wr,
input wire border_wr,
input wire zvpage_wr,
input wire vpage_wr,
input wire vconf_wr,
input wire gx_offsl_wr,
input wire gx_offsh_wr,
input wire gy_offsl_wr,
input wire gy_offsh_wr,
input wire t0x_offsl_wr,
input wire t0x_offsh_wr,
input wire t0y_offsl_wr,
input wire t0y_offsh_wr,
input wire t1x_offsl_wr,
input wire t1x_offsh_wr,
input wire t1y_offsl_wr,
input wire t1y_offsh_wr,
input wire tsconf_wr,
input wire palsel_wr,
input wire tmpage_wr,
input wire t0gpage_wr,
input wire t1gpage_wr,
input wire sgpage_wr,
input wire hint_beg_wr ,
input wire vint_begl_wr,
input wire vint_begh_wr,
// video parameters
output reg [7:0] border,
output reg [7:0] vpage,
output reg [7:0] vconf,
output reg [8:0] gx_offs,
output reg [8:0] gy_offs,
output reg [8:0] t0x_offs,
output reg [8:0] t0y_offs,
output reg [8:0] t1x_offs,
output reg [8:0] t1y_offs,
output reg [7:0] palsel,
output reg [7:0] hint_beg,
output reg [8:0] vint_beg,
output reg [7:0] tsconf,
output reg [7:0] tmpage,
output reg [7:0] t0gpage,
output reg [7:0] t1gpage,
output reg [7:0] sgpage
);
reg [7:0] vpage_r;
reg [7:0] vconf_r;
reg [7:0] t0gpage_r;
reg [7:0] t1gpage_r;
reg [8:0] gx_offs_r;
reg [8:0] t0x_offs_r;
reg [8:0] t1x_offs_r;
reg [7:0] palsel_r;
wire [8:0] vint_beg_inc = vint_beg + vint_inc;
wire [8:0] vint_beg_next = {(vint_beg_inc[8:6] == 3'b101) ? 3'b0 : vint_beg_inc[8:6], vint_beg_inc[5:0]}; // if over 319 lines, decrement 320
reg [3:0] vint_inc; `include "tune.v"
always @(posedge clk) begin
if (res) begin module video_ports
vint_beg <= 9'd0; (
vint_inc <= 4'b0; // clocks
end input wire clk,
else if (vint_begl_wr) vint_beg[7:0] <= d;
else if (vint_begh_wr) begin input wire [ 7:0] d,
vint_beg[8] <= d[0]; input wire res,
vint_inc <= d[7:4]; input wire int_start,
end input wire line_start_s,
else if (int_start) vint_beg <= vint_beg_next;
end // port write strobes
input wire zborder_wr,
always @(posedge clk) begin input wire border_wr,
if (res) begin input wire zvpage_wr,
vpage_r <= 8'h05; input wire vpage_wr,
vconf_r <= 8'h00; input wire vconf_wr,
gx_offs_r <= 9'b0; input wire gx_offsl_wr,
palsel_r <= 8'h0F; input wire gx_offsh_wr,
gy_offs <= 9'b0; input wire gy_offsl_wr,
tsconf <= 8'b0; input wire gy_offsh_wr,
hint_beg <= 8'd1; input wire t0x_offsl_wr,
end input wire t0x_offsh_wr,
else begin input wire t0y_offsl_wr,
if (zborder_wr ) border <= {palsel[3:0], 1'b0, d[2:0]}; input wire t0y_offsh_wr,
if (border_wr ) border <= d; input wire t1x_offsl_wr,
if (gy_offsl_wr ) gy_offs[7:0] <= d; input wire t1x_offsh_wr,
if (gy_offsh_wr ) gy_offs[8] <= d[0]; input wire t1y_offsl_wr,
if (t0y_offsl_wr) t0y_offs[7:0] <= d; input wire t1y_offsh_wr,
if (t0y_offsh_wr) t0y_offs[8] <= d[0]; input wire tsconf_wr,
if (t1y_offsl_wr) t1y_offs[7:0] <= d; input wire palsel_wr,
if (t1y_offsh_wr) t1y_offs[8] <= d[0]; input wire tmpage_wr,
if (tsconf_wr ) tsconf <= d; input wire t0gpage_wr,
if (tmpage_wr ) tmpage <= d; input wire t1gpage_wr,
if (sgpage_wr ) sgpage <= d; input wire sgpage_wr,
if (hint_beg_wr ) hint_beg <= d; input wire hint_beg_wr ,
input wire vint_begl_wr,
if (zvpage_wr ) vpage_r <= {6'b000001, d[3], 1'b1}; input wire vint_begh_wr,
if (vpage_wr ) vpage_r <= d;
if (vconf_wr ) vconf_r <= d; // video parameters
if (gx_offsl_wr ) gx_offs_r[7:0] <= d; output reg [7:0] border = 0,
if (gx_offsh_wr ) gx_offs_r[8] <= d[0]; output reg [7:0] vpage = 0,
if (palsel_wr ) palsel_r <= d; output reg [7:0] vconf = 0,
if (t0x_offsl_wr) t0x_offs_r[7:0] <= d; output reg [8:0] gx_offs = 0,
if (t0x_offsh_wr) t0x_offs_r[8] <= d[0]; output reg [8:0] gy_offs = 0,
if (t1x_offsl_wr) t1x_offs_r[7:0] <= d; output reg [8:0] t0x_offs = 0,
if (t1x_offsh_wr) t1x_offs_r[8] <= d[0]; output reg [8:0] t0y_offs = 0,
if (t0gpage_wr ) t0gpage_r <= d; output reg [8:0] t1x_offs = 0,
if (t1gpage_wr ) t1gpage_r <= d; output reg [8:0] t1y_offs = 0,
end output reg [7:0] palsel = 0,
end output reg [7:0] hint_beg = 0,
output reg [8:0] vint_beg = 0,
// latching regs at line start, delaying hires for 1 line output reg [7:0] tsconf = 0,
always @(posedge clk) begin output reg [7:0] tmpage = 0,
if (res) output reg [7:0] t0gpage = 0,
begin output reg [7:0] t1gpage = 0,
vpage <= 8'h05; output reg [7:0] sgpage = 0
vconf <= 8'h00; );
gx_offs <= 9'b0;
palsel <= 8'h0F; reg [7:0] vpage_r = 0;
end reg [7:0] vconf_r = 0;
else if (zvpage_wr) vpage <= {6'b000001, d[3], 1'b1}; reg [7:0] t0gpage_r = 0;
else if (line_start_s) begin reg [7:0] t1gpage_r = 0;
vpage <= vpage_r; reg [8:0] gx_offs_r = 0;
vconf <= vconf_r; reg [8:0] t0x_offs_r = 0;
gx_offs <= gx_offs_r; reg [8:0] t1x_offs_r = 0;
palsel <= palsel_r; reg [7:0] palsel_r = 0;
t0x_offs <= t0x_offs_r; reg [3:0] vint_inc = 0;
t1x_offs <= t1x_offs_r;
t0gpage <= t0gpage_r; wire [8:0] vint_beg_inc = vint_beg + vint_inc;
t1gpage <= t1gpage_r; wire [8:0] vint_beg_next = {(vint_beg_inc[8:6] == 3'b101) ? 3'b0 : vint_beg_inc[8:6], vint_beg_inc[5:0]}; // if over 319 lines, decrement 320
end
end always @(posedge clk or posedge res)
if (res)
endmodule begin
vint_beg <= 9'd0;
vint_inc <= 4'b0;
end
else if (vint_begl_wr)
vint_beg[7:0] <= d;
else if (vint_begh_wr)
begin
vint_beg[8] <= d[0];
vint_inc <= d[7:4];
end
else if (int_start)
vint_beg <= vint_beg_next;
always @(posedge clk or posedge res)
if (res)
begin
vpage_r <= 8'h05;
vconf_r <= 8'h00;
gx_offs_r <= 9'b0;
palsel_r <= 8'h0F;
gy_offs <= 9'b0;
tsconf <= 8'b0;
hint_beg <= 8'd1;
end
else
begin
if (zborder_wr ) border <= {palsel[3:0], 1'b0, d[2:0]};
if (border_wr ) border <= d;
if (gy_offsl_wr ) gy_offs[7:0] <= d;
if (gy_offsh_wr ) gy_offs[8] <= d[0];
if (t0y_offsl_wr) t0y_offs[7:0] <= d;
if (t0y_offsh_wr) t0y_offs[8] <= d[0];
if (t1y_offsl_wr) t1y_offs[7:0] <= d;
if (t1y_offsh_wr) t1y_offs[8] <= d[0];
if (tsconf_wr ) tsconf <= d;
if (tmpage_wr ) tmpage <= d;
if (sgpage_wr ) sgpage <= d;
if (hint_beg_wr ) hint_beg <= d;
if (zvpage_wr ) vpage_r <= {6'b000001, d[3], 1'b1};
if (vpage_wr ) vpage_r <= d;
if (vconf_wr ) vconf_r <= d;
if (gx_offsl_wr ) gx_offs_r[7:0] <= d;
if (gx_offsh_wr ) gx_offs_r[8] <= d[0];
if (palsel_wr ) palsel_r <= d;
if (t0x_offsl_wr) t0x_offs_r[7:0] <= d;
if (t0x_offsh_wr) t0x_offs_r[8] <= d[0];
if (t1x_offsl_wr) t1x_offs_r[7:0] <= d;
if (t1x_offsh_wr) t1x_offs_r[8] <= d[0];
if (t0gpage_wr ) t0gpage_r <= d;
if (t1gpage_wr ) t1gpage_r <= d;
end
// latching regs at line start, delaying hires for 1 line
always @(posedge clk or posedge res)
if (res)
begin
vpage <= 8'h05;
`ifdef FORCE_TEXT_MODE
vconf <= 8'h83;
`else
vconf <= 8'h00;
`endif
gx_offs <= 9'b0;
palsel <= 8'h0F;
end
else if (zvpage_wr)
vpage <= {6'b000001, d[3], 1'b1};
else if (line_start_s)
begin
vpage <= vpage_r;
`ifndef FORCE_TEXT_MODE
vconf <= vconf_r;
`endif
gx_offs <= gx_offs_r;
palsel <= palsel_r;
t0x_offs <= t0x_offs_r;
t1x_offs <= t1x_offs_r;
t0gpage <= t0gpage_r;
t1gpage <= t1gpage_r;
end
endmodule

View File

@ -1,84 +1,87 @@
// This module renders video data for output // This module renders video data for output
`include "tune.v"
module video_render module video_render
( (
// clocks // clocks
input wire clk, c1, input wire clk, c1,
// video controls // video controls
input wire hvpix, input wire hvpix,
input wire hvtspix, input wire hvtspix,
input wire nogfx, input wire nogfx,
input wire notsu, input wire notsu,
input wire gfxovr, input wire gfxovr,
input wire flash, input wire flash,
input wire hires, input wire hires,
input wire [3:0] psel, input wire [3:0] psel,
input wire [3:0] palsel, input wire [3:0] palsel,
// mode controls // mode controls
input wire [1:0] render_mode, input wire [1:0] render_mode,
// video data // video data
input wire [31:0] data, input wire [31:0] data,
input wire [ 7:0] border_in, input wire [ 7:0] border_in,
input wire [ 7:0] tsdata_in, input wire [ 7:0] tsdata_in,
output wire [ 7:0] vplex_out output wire [ 7:0] vplex_out
); );
localparam R_ZX = 2'h0; localparam R_ZX = 2'h0;
localparam R_HC = 2'h1; localparam R_HC = 2'h1;
localparam R_XC = 2'h2; localparam R_XC = 2'h2;
localparam R_TX = 2'h3; localparam R_TX = 2'h3;
// ZX graphics reg [3:0] temp;
wire [15:0] zx_gfx = data[15: 0];
wire [15:0] zx_atr = data[31:16]; // ZX graphics
wire zx_dot = zx_gfx[{psel[3], ~psel[2:0]}]; wire [15:0] zx_gfx = data[15: 0];
wire [7:0] zx_attr = ~psel[3] ? zx_atr[7:0] : zx_atr[15:8]; wire [15:0] zx_atr = data[31:16];
wire [7:0] zx_pix = {palsel, zx_attr[6], zx_dot ^ (flash & zx_attr[7]) ? zx_attr[2:0] : zx_attr[5:3]}; wire zx_dot = zx_gfx[{psel[3], ~psel[2:0]}];
wire [7:0] zx_attr = ~psel[3] ? zx_atr[7:0] : zx_atr[15:8];
// text graphics wire [7:0] zx_pix = {palsel, zx_attr[6], zx_dot ^ (flash & zx_attr[7]) ? zx_attr[2:0] : zx_attr[5:3]};
// (it uses common renderer with ZX, but different attributes)
wire [7:0] tx_pix = {palsel, zx_dot ? zx_attr[3:0] : zx_attr[7:4]}; // text graphics
// (uses common renderer with ZX, but different attributes)
// 16c graphics wire [7:0] tx_pix = {palsel, zx_dot ? zx_attr[3:0] : zx_attr[7:4]};
wire [3:0] hc_dot[0:3];
assign hc_dot[0] = data[ 7: 4]; // 16c graphics
assign hc_dot[1] = data[ 3: 0]; wire [3:0] hc_dot[0:3];
assign hc_dot[2] = data[15:12]; assign hc_dot[0] = data[ 7: 4];
assign hc_dot[3] = data[11: 8]; assign hc_dot[1] = data[ 3: 0];
wire [7:0] hc_pix = {palsel, hc_dot[psel[1:0]]}; assign hc_dot[2] = data[15:12];
assign hc_dot[3] = data[11: 8];
// 256c graphics wire [7:0] hc_pix = {palsel, hc_dot[psel[1:0]]};
wire [7:0] xc_dot[0:1];
assign xc_dot[0] = data[ 7: 0]; // 256c graphics
assign xc_dot[1] = data[15: 8]; wire [7:0] xc_dot[0:1];
wire [7:0] xc_pix = xc_dot[psel[0]]; assign xc_dot[0] = data[ 7: 0];
assign xc_dot[1] = data[15: 8];
// mode selects wire [7:0] xc_pix = xc_dot[psel[0]];
wire [7:0] pix[0:3];
assign pix[R_ZX] = zx_pix; // ZX // mode selects
assign pix[R_HC] = hc_pix; // 16c wire [7:0] pix[0:3];
assign pix[R_XC] = xc_pix; // 256c assign pix[R_ZX] = zx_pix; // ZX
assign pix[R_TX] = tx_pix; // text assign pix[R_HC] = hc_pix; // 16c
assign pix[R_XC] = xc_pix; // 256c
wire pixv[0:3]; assign pix[R_TX] = tx_pix; // text
assign pixv[R_ZX] = zx_dot ^ (flash & zx_attr[7]);
assign pixv[R_HC] = |hc_dot[psel[1:0]]; wire pixv[0:3];
assign pixv[R_XC] = |xc_dot[psel[0]]; assign pixv[R_ZX] = zx_dot ^ (flash & zx_attr[7]);
assign pixv[R_TX] = zx_dot; assign pixv[R_HC] = |hc_dot[psel[1:0]];
assign pixv[R_XC] = |xc_dot[psel[0]];
// video plex muxer assign pixv[R_TX] = zx_dot;
wire tsu_visible = (|tsdata_in[3:0] && !notsu);
wire gfx_visible = (pixv[render_mode] && !nogfx); // video plex muxer
wire [7:0] video1 = tsu_visible ? tsdata_in : (nogfx ? border_in : pix[render_mode]); wire tsu_visible = (|tsdata_in[3:0] && !notsu);
wire [7:0] video2 = gfx_visible ? pix[render_mode] : (tsu_visible ? tsdata_in : border_in); wire gfx_visible = (pixv[render_mode] && !nogfx);
wire [7:0] video = hvpix ? (gfxovr ? video2 : video1) : ((hvtspix && tsu_visible) ? tsdata_in : border_in); wire [7:0] video1 = tsu_visible ? tsdata_in : (nogfx ? border_in : pix[render_mode]);
assign vplex_out = hires ? {temp, video[3:0]} : video; // in hi-res plex contains two pixels 4 bits each wire [7:0] video2 = gfx_visible ? pix[render_mode] : (tsu_visible ? tsdata_in : border_in);
wire [7:0] video = hvpix ? (gfxovr ? video2 : video1) : ((hvtspix && tsu_visible) ? tsdata_in : border_in);
reg [3:0] temp; assign vplex_out = hires ? {temp, video[3:0]} : video; // in hi-res plex contains two pixels 4 bits each
always @(posedge clk) if (c1) temp <= video[3:0];
always @(posedge clk) if (c1)
endmodule temp <= video[3:0];
endmodule

View File

@ -1,169 +1,243 @@
`include "tune.v"
// This module generates all video raster signals
// This module generates video raster signals
module video_sync module video_sync
( (
// clocks // clocks
input wire clk, f1, c0, c1, c3, pix_stb, input wire clk, f1, c0, c3, pix_stb,
// video parameters
input wire [8:0] hpix_beg,
input wire [8:0] hpix_end,
input wire [8:0] vpix_beg,
input wire [8:0] vpix_end,
input wire [8:0] hpix_beg_ts,
input wire [8:0] hpix_end_ts,
input wire [8:0] vpix_beg_ts,
input wire [8:0] vpix_end_ts,
input wire [4:0] go_offs,
input wire [1:0] x_offs,
input wire [7:0] hint_beg,
input wire [8:0] vint_beg,
input wire [7:0] cstart,
input wire [8:0] rstart,
// video syncs
output reg hsync,
output reg vsync,
// video controls
input wire nogfx,
output wire v_pf,
output wire hpix,
output wire vpix,
output wire v_ts,
output wire hvpix,
output wire hvtspix,
output wire tv_hblank,
output wire tv_vblank,
output wire frame_start,
output wire line_start_s,
output wire pix_start,
output wire ts_start,
output wire frame,
output wire flash,
// video counters
output wire [8:0] ts_raddr,
output reg [8:0] lcount,
output reg [7:0] cnt_col,
output reg [8:0] cnt_row,
output reg cptr,
output reg [3:0] scnt,
// DRAM
input wire video_pre_next,
output reg video_go,
// ZX controls
input wire y_offs_wr,
output wire int_start
);
localparam HSYNC_BEG = 9'd11;
localparam HSYNC_END = 9'd43;
localparam HBLNK_BEG = 9'd00;
localparam HBLNK_END = 9'd88;
localparam HSYNCV_BEG = 9'd5;
localparam HSYNCV_END = 9'd31;
localparam HBLNKV_END = 9'd42;
localparam HPERIOD = 9'd448;
localparam VSYNC_BEG = 9'd08;
localparam VSYNC_END = 9'd11;
localparam VBLNK_BEG = 9'd00;
localparam VBLNK_END = 9'd32;
localparam VPERIOD = 9'd320;
// counters
reg [8:0] hcount = 0;
reg [8:0] vcount = 0;
// horizontal TV (7 MHz)
always @(posedge clk) if (c3) hcount <= line_start ? 9'b0 : hcount + 9'b1;
// vertical TV (15.625 kHz)
always @(posedge clk) if (line_start_s) vcount <= (vcount == (VPERIOD - 1)) ? 9'b0 : vcount + 9'b1;
// column address for DRAM
always @(posedge clk) begin
if (line_start2) begin
cnt_col <= cstart;
cptr <= 1'b0;
end
else if (video_pre_next) begin
cnt_col <= cnt_col + 8'b1;
cptr <= ~cptr;
end
end
// row address for DRAM
always @(posedge clk) begin if (c3)
if (vis_start || (line_start && y_offs_wr_r)) cnt_row <= rstart;
else if (line_start && vpix) cnt_row <= cnt_row + 9'b1;
end
// pixel counter
always @(posedge clk) if (pix_stb) scnt <= pix_start ? 4'b0 : scnt + 4'b1; // f1 or c3
// TS-line counter
assign ts_raddr = hcount - hpix_beg_ts;
always @(posedge clk) if (ts_start_coarse) lcount <= vcount - vpix_beg_ts + 9'b1;
// Y offset re-latch trigger
reg y_offs_wr_r;
always @(posedge clk) begin
if (y_offs_wr) y_offs_wr_r <= 1'b1;
else if (line_start_s) y_offs_wr_r <= 1'b0;
end
// FLASH generator
reg [4:0] flash_ctr;
assign frame = flash_ctr[0];
assign flash = flash_ctr[4];
always @(posedge clk) begin
if (frame_start && c3) begin
flash_ctr <= flash_ctr + 5'b1;
end
end
// sync strobes
wire hs = (hcount >= HSYNC_BEG) && (hcount < HSYNC_END);
wire vs = (vcount >= VSYNC_BEG) && (vcount < VSYNC_END);
assign tv_hblank = (hcount > HBLNK_BEG) && (hcount <= HBLNK_END);
assign tv_vblank = (vcount >= VBLNK_BEG) && (vcount < VBLNK_END);
assign hvpix = hpix && vpix;
assign hpix = (hcount >= hpix_beg) && (hcount < hpix_end);
assign vpix = (vcount >= vpix_beg) && (vcount < vpix_end);
assign hvtspix = htspix && vtspix; // video parameters
wire htspix = (hcount >= hpix_beg_ts) && (hcount < hpix_end_ts); input wire [8:0] hpix_beg,
wire vtspix = (vcount >= vpix_beg_ts) && (vcount < vpix_end_ts); input wire [8:0] hpix_end,
input wire [8:0] vpix_beg,
input wire [8:0] vpix_end,
input wire [8:0] hpix_beg_ts,
input wire [8:0] hpix_end_ts,
input wire [8:0] vpix_beg_ts,
input wire [8:0] vpix_end_ts,
input wire [4:0] go_offs,
input wire [1:0] x_offs,
input wire [7:0] hint_beg,
input wire [8:0] vint_beg,
input wire [7:0] cstart,
input wire [8:0] rstart,
assign v_ts = (vcount >= (vpix_beg_ts - 1)) && (vcount < (vpix_end_ts - 1)); // vertical TS window // video syncs
assign v_pf = (vcount >= (vpix_beg_ts - 17)) && (vcount < (vpix_end_ts - 9)); // vertical tilemap prefetch window output reg hsync,
output reg vsync,
always @(posedge clk) video_go <= (hcount >= (hpix_beg - go_offs - x_offs)) && (hcount < (hpix_end - go_offs - x_offs + 4)) && vpix && !nogfx; output reg csync,
wire line_start = hcount == (HPERIOD - 1); // video controls
assign line_start_s = line_start && c3; input wire cfg_60hz,
wire line_start2 = hcount == (HSYNC_END - 1); input wire vga_on,
assign frame_start = line_start && (vcount == (VPERIOD - 1)); output reg v60hz = 0,
wire vis_start = line_start && (vcount == (VBLNK_END - 1)); input wire nogfx,
assign pix_start = hcount == (hpix_beg - x_offs - 1); output wire v_pf,
wire ts_start_coarse = hcount == (hpix_beg_ts - 1); output wire hpix,
assign ts_start = c3 && ts_start_coarse; output wire vpix,
assign int_start = (hcount == {hint_beg, 1'b0}) && (vcount == vint_beg) && c0; output wire v_ts,
output wire hvpix,
always @(posedge clk) begin output wire hvtspix,
hsync <= hs; output wire tv_blank,
vsync <= vs; output wire vga_blank,
end output wire vga_line,
output wire frame_start,
endmodule output wire line_start_s,
output wire pix_start,
output wire ts_start,
output wire frame,
output wire flash,
// video counters
output wire [9:0] vga_cnt_in,
output wire [9:0] vga_cnt_out,
output wire [8:0] ts_raddr,
output reg [8:0] lcount = 0,
output reg [7:0] cnt_col = 0,
output reg [8:0] cnt_row = 0,
output reg cptr = 0,
output reg [3:0] scnt = 0,
`ifdef PENT_312
output wire [4:0] hcnt,
output wire upper8,
`endif
// DRAM
input wire video_pre_next,
output reg video_go = 0,
// ZX controls
input wire y_offs_wr,
output wire int_start
);
localparam HSYNC_BEG = 9'd11;
localparam HSYNC_END = 9'd43;
localparam HBLNK_BEG = 9'd00;
localparam HBLNK_END = 9'd88;
localparam HSYNCV_BEG = 9'd5;
localparam HSYNCV_END = 9'd31;
localparam HBLNKV_END = 9'd42;
localparam HPERIOD = 9'd448;
localparam VSYNC_BEG_50 = 9'd08;
localparam VSYNC_END_50 = 9'd11;
localparam VBLNK_BEG_50 = 9'd00;
`ifdef PENT_312
localparam VBLNK_END_50 = 9'd24;
localparam VPERIOD_50 = 9'd312;
`else
localparam VBLNK_END_50 = 9'd32;
localparam VPERIOD_50 = 9'd320;
`endif
localparam VSYNC_BEG_60 = 9'd04;
localparam VSYNC_END_60 = 9'd07;
localparam VBLNK_BEG_60 = 9'd00;
localparam VBLNK_END_60 = 9'd22;
localparam VPERIOD_60 = 9'd262;
wire [8:0] vsync_beg = v60hz ? VSYNC_BEG_60 : VSYNC_BEG_50;
wire [8:0] vsync_end = v60hz ? VSYNC_END_60 : VSYNC_END_50;
wire [8:0] vblnk_beg = v60hz ? VBLNK_BEG_60 : VBLNK_BEG_50;
wire [8:0] vblnk_end = v60hz ? VBLNK_END_60 : VBLNK_END_50;
wire [8:0] vperiod = v60hz ? VPERIOD_60 : VPERIOD_50;
`ifdef PENT_312
assign hcnt = hcount[4:0];
assign upper8 = vcount < 8;
`endif
reg [8:0] hcount = 0;
reg [8:0] vcount = 0;
reg [8:0] cnt_out = 0;
reg vga_hblank = 0;
reg vga_vblank = 0;
wire line_start = hcount == (HPERIOD - 1);
wire line_start2 = hcount == (HSYNC_END - 1);
assign line_start_s = line_start && c3;
assign frame_start = line_start && (vcount == (vperiod - 1));
wire vis_start = line_start && (vcount == (vblnk_end - 1));
assign pix_start = hcount == (hpix_beg - x_offs - 1);
wire ts_start_coarse = hcount == (hpix_beg_ts - 1);
assign ts_start = c3 && ts_start_coarse;
assign int_start = (hcount == {hint_beg, 1'b0}) && (vcount == vint_beg) && c0;
wire vga_pix_start = ((hcount == (HBLNKV_END)) || (hcount == (HBLNKV_END + HPERIOD/2)));
wire hs_vga = ((hcount >= HSYNCV_BEG) && (hcount < HSYNCV_END)) || ((hcount >= (HSYNCV_BEG + HPERIOD/2)) && (hcount < (HSYNCV_END + HPERIOD/2)));
assign vga_line = (hcount >= HPERIOD/2);
wire hs = (hcount >= HSYNC_BEG) && (hcount < HSYNC_END);
wire vs = (vcount >= vsync_beg) && (vcount < vsync_end);
assign vga_cnt_in = {vcount[0], hcount - HBLNK_END};
assign vga_cnt_out = {~vcount[0], cnt_out};
wire tv_hblank = (hcount > HBLNK_BEG) && (hcount <= HBLNK_END);
wire tv_vblank = (vcount >= vblnk_beg) && (vcount < vblnk_end);
assign vga_blank = vga_hblank || vga_vblank;
assign tv_blank = tv_hblank || tv_vblank;
// horizontal TV (7 MHz)
always @(posedge clk) if (c3)
hcount <= line_start ? 9'b0 : hcount + 9'b1;
// vertical TV (15.625 kHz)
always @(posedge clk) if (line_start_s)
vcount <= (vcount == (vperiod - 1)) ? 9'b0 : vcount + 9'b1;
// horizontal VGA (14MHz)
always @(posedge clk) if (f1)
cnt_out <= vga_pix_start && c3 ? 9'b0 : cnt_out + 9'b1;
// column address for DRAM
always @(posedge clk)
begin
if (line_start2)
begin
cnt_col <= cstart;
cptr <= 1'b0;
end
else if (video_pre_next)
begin
cnt_col <= cnt_col + 8'b1;
cptr <= ~cptr;
end
end
// row address for DRAM
reg y_offs_wr_r;
always @(posedge clk) if (c3)
if (vis_start || (line_start && y_offs_wr_r))
cnt_row <= rstart;
else
if (line_start && vpix)
cnt_row <= cnt_row + 9'b1;
// pixel counter
always @(posedge clk) if (pix_stb) // f1 or c3
scnt <= pix_start ? 4'b0 : scnt + 4'b1;
// TS-line counter
assign ts_raddr = hcount - hpix_beg_ts;
always @(posedge clk)
if (ts_start_coarse)
lcount <= vcount - vpix_beg_ts + 9'b1;
// Y offset re-latch trigger
always @(posedge clk)
if (y_offs_wr)
y_offs_wr_r <= 1'b1;
else if (line_start_s)
y_offs_wr_r <= 1'b0;
// FLASH generator
reg [4:0] flash_ctr;
assign frame = flash_ctr[0];
assign flash = flash_ctr[4];
always @(posedge clk)
if (frame_start && c3)
begin
flash_ctr <= flash_ctr + 5'b1;
// re-sync 60Hz mode selector
`ifdef FORCE_60HZ
v60hz <= 1'b1;
`elsif ENABLE_60HZ
v60hz <= !cfg_60hz;
`else
v60hz <= 1'b0;
`endif
end
// sync strobes
wire vga_hblank1 = (cnt_out > 9'd359);
always @(posedge clk) if (f1) // fix me - bydlocode !!!
vga_hblank <= vga_hblank1;
assign hvpix = hpix && vpix;
assign hpix = (hcount >= hpix_beg) && (hcount < hpix_end);
assign vpix = (vcount >= vpix_beg) && (vcount < vpix_end);
wire htspix = (hcount >= hpix_beg_ts) && (hcount < hpix_end_ts);
wire vtspix = (vcount >= vpix_beg_ts) && (vcount < vpix_end_ts);
assign hvtspix = htspix && vtspix;
assign v_ts = (vcount >= (vpix_beg_ts - 1)) && (vcount < (vpix_end_ts - 1)); // vertical TS window
assign v_pf = (vcount >= (vpix_beg_ts - 17)) && (vcount < (vpix_end_ts - 9)); // vertical tilemap prefetch window
always @(posedge clk)
video_go <= (hcount >= (hpix_beg - go_offs - x_offs)) && (hcount < (hpix_end - go_offs - x_offs + 4)) && vpix && !nogfx;
always @(posedge clk) if (line_start_s) // fix me - bydlocode !!!
vga_vblank <= tv_vblank;
always @(posedge clk)
begin
hsync <= vga_on ? hs_vga : hs;
vsync <= vs;
csync <= ~(vs ^ hs);
end
endmodule

File diff suppressed because it is too large Load Diff

View File

@ -1,383 +1,459 @@
`include "tune.v"
// This is the Tile Sprite Processing Unit
// This is the Tile Sprite Processing Unit
// Tiles map address:
// bits desc // Tiles map address:
// 20:13 tmpage // bits desc
// 12: // 20:13 tmpage
// 12:
// Graphics address:
// bits desc // Graphics address:
// 20:13 Xgpage // bits desc
// 15:7 line (bits 15:13 are added) - 512 lines // 20:13 Xgpage
// 6:0 word within line - 128 words = 512 pixels // 15:7 line (bits 15:13 are added) - 512 lines
// 6:0 word within line - 128 words = 512 pixels
module video_ts
( module video_ts
// clocks
input wire clk,
// video controls
input wire start,
input wire [8:0] line, // = vcount - vpix_beg + 9'b1;
input wire v_ts,
input wire v_pf, // vertical tilemap prefetch window
// video config
input wire [7:0] tsconf,
input wire [7:0] t0gpage,
input wire [7:0] t1gpage,
input wire [7:0] sgpage,
input wire [7:0] tmpage,
input wire [5:0] num_tiles,
input wire [8:0] t0x_offs,
input wire [8:0] t1x_offs,
input wire [8:0] t0y_offs,
input wire [8:0] t1y_offs,
input wire [1:0] t0_palsel,
input wire [1:0] t1_palsel,
// SFYS interface
input wire [7:0] sfile_addr_in,
input wire [15:0] sfile_data_in,
input wire sfile_we,
// renderer interface
output wire tsr_go,
output wire [5:0] tsr_addr, // graphics address within the line
output wire [8:0] tsr_line, // bitmap line
output wire [7:0] tsr_page, // bitmap 1st page
output wire [8:0] tsr_x, // addr in buffer (0-359 visibles)
output wire [2:0] tsr_xs, // size (8-64 pix)
output wire tsr_xf, // X flip
output wire [3:0] tsr_pal, // palette
input wire tsr_rdy, // renderer is done and ready to receive a new task
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire dram_next,
input wire [15:0] dram_rdata
);
// config
wire s_en = tsconf[7];
wire t1_en = tsconf[6];
wire t0_en = tsconf[5];
wire t1z_en = tsconf[3];
wire t0z_en = tsconf[2];
// TS renderer interface
assign tsr_go = sprite_go || tile_go;
assign tsr_x = sprites ? sprites_x : tile_x;
assign tsr_xs = sprites ? sprites_xs : 3'd0;
assign tsr_xf = sprites ? sprites_xf : t_xflp;
assign tsr_page = sprites ? sgpage : tile_page;
assign tsr_line = sprites ? sprites_line : tile_line;
assign tsr_addr = sprites ? sprites_addr : tile_addr;
assign tsr_pal = sprites ? s_pal : tile_pal;
// Layer selectors control
localparam LAYERS = 6; // Total number of layers to process
localparam TM = 0; // Individual layers
localparam S0 = 1;
localparam T0 = 2;
localparam S1 = 3;
localparam T1 = 4;
localparam S2 = 5;
wire tmap = layer_active[TM];
wire sprites = layer_active[S0] || layer_active[S1] || layer_active[S2];
wire tiles = layer_active[T0] || layer_active[T1];
reg [LAYERS-1:0] layer;
always @(posedge clk)
if (start)
layer <= 1;
else if (|(layer & layer_skip))
layer <= {layer[LAYERS-2:0], 1'b0};
reg [LAYERS-1:0] layer_active;
always @(posedge clk)
if (start)
layer_active <= 0;
else
layer_active <= layer & ~layer_skip;
wire [LAYERS-1:0] layer_enabled = {s_en, t1_en, s_en, t0_en, s_en, t1_en || t0_en};
wire [LAYERS-1:0] layer_allowed = {{5{v_ts}}, v_pf};
wire [LAYERS-1:0] layer_end = {spr_end[2], tile_end[1], spr_end[1], tile_end[0], spr_end[0], tm_end};
reg [LAYERS-1:0] layer_skip;
always @(posedge clk)
if (start)
layer_skip <= ~(layer_enabled & layer_allowed);
else
layer_skip <= layer_skip | layer_end;
// --- Tile map prefetch ---
// DRAM controls
assign dram_addr = {tmpage, tm_b_row, tm_layer, tm_b_line, tm_num}; // 20:13 - page, 12:7 - row, 6 - layer, 5:0 - column (burst number : number in burst)
assign dram_req = tmap;
// TMB control
wire [8:0] tmb_waddr = {tm_line[4:3], tm_b_line, tm_num, tm_layer}; // 8:7 - buffer #, 6:4 - burst number (of 8 bursts), 3:1 - number in burst (8 tiles per burst), 0 - layer
wire [8:0] tm_line = line + 9'd16;
wire [2:0] tm_b_line = tm_line[2:0];
wire [5:0] tm_b_row = tm_line[8:3] + (tm_layer ? t1y_offs[8:3] : t0y_offs[8:3]);
wire [2:0] tm_num = tm_x[2:0];
wire tm_layer = tm_x[3];
// internal layers control
wire tm_end = tm_x == (t1_en ? 5'd16 : 5'd8);
wire tm_next = dram_next && tmap;
reg [1:0] m_layer;
always @(posedge clk)
if (start)
m_layer <= 2'b1;
else if (tm_end)
m_layer <= {m_layer[0], 1'b0};
// tilemap X coordinate
reg [4:0] tm_x;
always @(posedge clk)
if (start)
tm_x <= t0_en ? 5'd0 : 5'd8;
else if (tm_next)
tm_x <= tm_x + 5'd1;
// --- Tiles ---
// tile descriptor fields
wire [11:0] t_tnum = tmb_rdata[11:0];
wire [1:0] t_pal = tmb_rdata[13:12];
wire t_xflp = tmb_rdata[14];
wire t_yflp = tmb_rdata[15];
// TSR control
wire [7:0] tile_page = t_sel ? t0gpage : t1gpage;
wire [8:0] tile_line = {t_tnum[11:6], (t_line[2:0] ^ {3{t_yflp}})};
wire [5:0] tile_addr = t_tnum[5:0];
wire [8:0] tile_x = {(tx - 6'd1), 3'd0} - tx_offs[2:0];
wire [3:0] tile_pal = {t_sel ? t0_palsel : t1_palsel, t_pal};
// TMB control
wire [8:0] tmb_raddr = {t_line[4:3], tx + tx_offs[8:3], ~t_sel};
// layer parameter selectors
wire [8:0] tx_offs = t_sel ? t0x_offs : t1x_offs;
wire [3:0] ty_offs = t_sel ? t0y_offs[2:0] : t1y_offs[2:0];
wire t_sel = t_layer[0];
// internal layers control
wire [1:0] tile_end = {2{t_layer_end}} & t_layer[1:0];
wire t_layer_end = tx == num_tiles;
wire t_layer_start = start || t_layer_end;
reg [1:0] t_layer;
always @(posedge clk)
if (start)
t_layer <= t0_en ? 2'b01 : 2'b10;
else if (t_layer_end)
t_layer <= {t_layer[0], 1'b0};
// TMBUF control
// condition write to tx write to tm_valid
// t_layer_start 0 TM_PRE_VALID
// tm_pre_valid tx+1 TM_VALID
// tile_skip tx+1 -
// tile_go tx+1 TM_VALID
// tile_wait tx-1 TM_PRE_VALID
localparam TM_PRE_VALID = 2'b01;
localparam TM_VALID = 2'b10;
wire tile_go = tile_good && tsr_allowed;
wire tile_wait = tile_good && !tsr_allowed;
wire tile_good = tm_valid && tile_valid;
wire tile_skip = tm_valid && !tile_valid;
wire tsr_allowed = tiles && tsr_rdy;
wire tile_valid = |t_tnum || (t_sel ? t0z_en : t1z_en);
wire tm_pre_valid = tm_valid_r[0];
wire tm_valid = tm_valid_r[1];
reg [1:0] tm_valid_r;
always @(posedge clk)
if (t_layer_start || tile_wait)
tm_valid_r <= TM_PRE_VALID;
else if (tm_pre_valid || tile_go)
tm_valid_r <= TM_VALID;
reg [5:0] tx;
always @(posedge clk)
if (t_layer_start)
tx <= 6'd0;
else if (tm_pre_valid || tile_skip || tile_go)
tx <= tx + 6'd1;
else if (tile_wait)
tx <= tx - 6'd1;
// tile Y geometry
wire [4:0] t_line = line[4:0] + ty_offs;
// --- Sprites ---
// sprite descriptor fields
// R0
wire [8:0] s_ycrd = sfile_rdata[8:0];
wire [2:0] s_ysz = sfile_rdata[11:9];
wire s_act = sfile_rdata[13];
wire s_leap = sfile_rdata[14];
wire s_yflp = sfile_rdata[15];
// R1
wire [8:0] s_xcrd = sfile_rdata[8:0];
wire [2:0] s_xsz = sfile_rdata[11:9];
wire s_xflp = sfile_rdata[15];
// R2
wire [11:0] s_tnum = sfile_rdata[11:0];
wire [3:0] s_pal = sfile_rdata[15:12];
// TSR control
reg [8:0] sprites_x;
reg [2:0] sprites_xs;
reg sprites_xf;
wire [5:0] sprites_addr = s_tnum[5:0];
// internal layers control
wire [2:0] spr_end = ({3{s_layer_end}} & s_layer[2:0]) | {3{sprites_last}};
wire s_layer_end = (sr0_valid && !spr_valid && s_leap) || (sprite_go && s_leap_r);
wire sprites_last = sr0_valid && sprites_last_r;
reg sprites_last_r;
always @(posedge clk) sprites_last_r <= sreg == 8'd255;
reg [2:0] s_layer;
always @(posedge clk)
if (start)
s_layer <= 3'b1;
else if (s_layer_end)
s_layer <= {s_layer[1:0], 1'b0};
// SFile registers control
// condition write to sreg write to sr_valid action
// start 0 SR0_PRE_VALID Start
// sr0_pre_valid sreg+3 SR0_VALID SR0 pre-read
// sr0_valid && !spr_valid sreg+3 - Skip sprite
// sr0_valid && spr_valid sreg-2 SR1_PRE_VALID SR1 pre-read
// sr1_pre_valid sreg+1 SR1_VALID SR1 read
// sr1_valid sreg+1 SR2_VALID SR2 pre-read
// sr2_valid && !tsr_rdy - - Wait for TSR ready
// sr2_valid && tsr_rdy sreg+1 SR0_PRE_VALID Next sprite
// sprites_last - NONE_VALID End
localparam NONE_VALID = 5'b00000;
localparam SR0_PRE_VALID = 5'b00001;
localparam SR0_VALID = 5'b00010;
localparam SR1_PRE_VALID = 5'b00100;
localparam SR1_VALID = 5'b01000;
localparam SR2_VALID = 5'b10000;
wire sprite_go = sr2_valid && sprites && tsr_rdy; // kick to renderer
wire spr_valid = s_visible && s_act;
wire sr0_pre_valid = sr_valid[0];
wire sr0_valid = sr_valid[1];
wire sr1_pre_valid = sr_valid[2];
wire sr1_valid = sr_valid[3];
wire sr2_valid = sr_valid[4];
reg [4:0] sr_valid;
always @(posedge clk)
if (start)
sr_valid <= SR0_PRE_VALID;
else if (sprites_last)
sr_valid <= NONE_VALID;
else if (sr0_pre_valid)
sr_valid <= SR0_VALID;
else if (sr0_valid && spr_valid)
sr_valid <= SR1_PRE_VALID;
else if (sr1_pre_valid)
sr_valid <= SR1_VALID;
else if (sr1_valid)
sr_valid <= SR2_VALID;
else if (sprite_go)
sr_valid <= SR0_PRE_VALID;
reg [7:0] sreg;
always @(posedge clk)
if (start)
sreg <= 8'd0;
else if (sr0_pre_valid)
sreg <= sreg + 8'd3;
else if (sr0_valid)
sreg <= spr_valid ? (sreg - 8'd2) : (sreg + 8'd3);
else if (sr1_pre_valid || sprite_go)
sreg <= sreg + 8'd1;
// SFile control
reg [5:0] s_bmline_offset_r;
reg s_leap_r;
always @(posedge clk) begin
if (sr0_valid)
begin
s_leap_r <= s_leap;
s_bmline_offset_r <= s_bmline_offset;
end
if (sr1_valid)
begin
sprites_x <= s_xcrd;
sprites_xs <= s_xsz;
sprites_xf <= s_xflp;
end
end
// sprite Y geometry
wire [8:0] s_line = line - s_ycrd; // visible line of sprite in current video line
wire s_visible = (s_line <= s_ymax); // check if sprite line is within Y size
wire [5:0] s_ymax = {s_ysz, 3'b111};
wire [8:0] sprites_line = {s_tnum[11:6], 3'b0} + s_bmline_offset_r;
wire [5:0] s_bmline_offset = s_yflp ? (s_ymax - s_line[5:0]) : s_line[5:0];
// SFile
wire [15:0] sfile_rdata;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) video_sfile
( (
.clock (clk), // clocks
.address_a (sfile_addr_in), input wire clk,
.data_a (sfile_data_in),
.wren_a (sfile_we), // video controls
.address_b (sreg), input wire start,
.q_b (sfile_rdata) input wire [8:0] line, // = vcount - vpix_beg + 9'b1;
); input wire v_ts,
input wire v_pf, // vertical tilemap prefetch window
// 4 buffers * 2 tile-planes * 64 tiles * 16 bits (9x16) - used to prefetch tiles
// (2 altdprams) // video config
wire [15:0] tmb_rdata; input wire [7:0] tsconf,
dpram #(.DATAWIDTH(16), .ADDRWIDTH(9)) video_tmbuf input wire [7:0] t0gpage,
( input wire [7:0] t1gpage,
.clock (clk), input wire [7:0] sgpage,
.address_a (tmb_waddr), input wire [7:0] tmpage,
.data_a (dram_rdata), input wire [5:0] num_tiles,
.wren_a (tm_next), input wire [8:0] t0x_offs,
.address_b (tmb_raddr), input wire [8:0] t1x_offs,
.q_b (tmb_rdata) input wire [8:0] t0y_offs,
input wire [8:0] t1y_offs,
input wire [1:0] t0_palsel,
input wire [1:0] t1_palsel,
// SFYS interface
input wire [7:0] sfile_addr_in,
input wire [15:0] sfile_data_in,
input wire sfile_we,
// renderer interface
output wire tsr_go,
output wire [5:0] tsr_addr, // graphics address within the line
output wire [8:0] tsr_line, // bitmap line
output wire [7:0] tsr_page, // bitmap 1st page
output wire [8:0] tsr_x, // addr in buffer (0-359 visibles)
output wire [2:0] tsr_xs, // size (8-64 pix)
output wire tsr_xf, // X flip
output wire [3:0] tsr_pal, // palette
input wire tsr_rdy, // renderer is done and ready to receive a new task
// DRAM interface
output wire [20:0] dram_addr,
output wire dram_req,
input wire dram_next,
input wire [15:0] dram_rdata
); );
endmodule // config
wire s_en = tsconf[7];
wire t1_en = tsconf[6];
wire t0_en = tsconf[5];
wire t1z_en = tsconf[3];
wire t0z_en = tsconf[2];
// tile descriptor fields
wire [15:0] tmb_rdata;
wire [11:0] t_tnum = tmb_rdata[11:0];
wire [1:0] t_pal = tmb_rdata[13:12];
wire t_xflp = tmb_rdata[14];
wire t_yflp = tmb_rdata[15];
// Layer selectors control
localparam LAYERS = 6; // total number of layers to process
localparam TM = 0; // Individual layers
localparam S0 = 1;
localparam T0 = 2;
localparam S1 = 3;
localparam T1 = 4;
localparam S2 = 5;
reg [LAYERS-1:0] layer_active;
reg [LAYERS-1:0] layer_skip;
wire tmap = layer_active[TM];
wire sprites = layer_active[S0] || layer_active[S1] || layer_active[S2];
wire tiles = layer_active[T0] || layer_active[T1];
reg [LAYERS-1:0] layer;
always @(posedge clk)
if (start)
layer <= 1;
else if (|(layer & layer_skip))
layer <= {layer[LAYERS-2:0], 1'b0};
always @(posedge clk)
if (start)
layer_active <= 0;
else
layer_active <= layer & ~layer_skip;
wire [2:0] spr_end;
wire [1:0] tile_end;
wire tm_end;
wire [LAYERS-1:0] layer_enabled = {s_en, t1_en, s_en, t0_en, s_en, t1_en || t0_en};
wire [LAYERS-1:0] layer_allowed = {{5{v_ts}}, v_pf};
wire [LAYERS-1:0] layer_end = {spr_end[2], tile_end[1], spr_end[1], tile_end[0], spr_end[0], tm_end};
always @(posedge clk)
if (start)
layer_skip <= ~(layer_enabled & layer_allowed);
else
layer_skip <= layer_skip | layer_end;
// --- Tile map prefetch ---
// TMB control
reg [4:0] tm_x;
wire [8:0] tm_line = line + 9'd16;
wire [2:0] tm_b_line = tm_line[2:0];
wire [2:0] tm_num = tm_x[2:0];
wire tm_layer = tm_x[3];
wire [8:0] tmb_waddr = {tm_line[4:3], tm_b_line, tm_num, tm_layer}; // 8:7 - buffer #, 6:4 - burst number (of 8 bursts), 3:1 - number in burst (8 tiles per burst), 0 - layer
wire [5:0] tm_b_row = tm_line[8:3] + (tm_layer ? t1y_offs[8:3] : t0y_offs[8:3]);
// DRAM controls
assign dram_addr = {tmpage, tm_b_row, tm_layer, tm_b_line, tm_num}; // 20:13 - page, 12:7 - row, 6 - layer, 5:0 - column (burst number : number in burst)
assign dram_req = tmap;
// internal layers control
assign tm_end = tm_x == (t1_en ? 5'd16 : 5'd8);
wire tm_next = dram_next && tmap;
reg [1:0] m_layer;
always @(posedge clk)
if (start)
m_layer <= 2'b1;
else if (tm_end)
m_layer <= {m_layer[0], 1'b0};
// tilemap X coordinate
always @(posedge clk)
if (start)
tm_x <= t0_en ? 5'd0 : 5'd8;
else if (tm_next)
tm_x <= tm_x + 5'd1;
// --- Tiles ---
// layer parameter selectors
reg [1:0] t_layer;
reg [5:0] tx;
wire t_sel = t_layer[0];
wire [8:0] tx_offs = t_sel ? t0x_offs : t1x_offs;
wire [3:0] ty_offs = t_sel ? t0y_offs[2:0] : t1y_offs[2:0];
// TSR control
wire [4:0] t_line;
wire [7:0] tile_page = t_sel ? t0gpage : t1gpage;
wire [8:0] tile_line = {t_tnum[11:6], (t_line[2:0] ^ {3{t_yflp}})};
wire [5:0] tile_addr = t_tnum[5:0];
wire [8:0] tile_x = {(tx - 6'd1), 3'd0} - tx_offs[2:0];
wire [3:0] tile_pal = {t_sel ? t0_palsel : t1_palsel, t_pal};
// TMB control
wire [8:0] tmb_raddr = {t_line[4:3], tx + tx_offs[8:3], ~t_sel};
// internal layers control
wire t_layer_end = tx == num_tiles;
wire t_layer_start = start || t_layer_end;
assign tile_end = {2{t_layer_end}} & t_layer[1:0];
always @(posedge clk)
if (start)
t_layer <= t0_en ? 2'b01 : 2'b10;
else if (t_layer_end)
t_layer <= {t_layer[0], 1'b0};
// TMBUF control
// condition write to tx write to tm_valid
// t_layer_start 0 TM_PRE_VALID
// tm_pre_valid tx+1 TM_VALID
// tile_skip tx+1 -
// tile_go tx+1 TM_VALID
// tile_wait tx-1 TM_PRE_VALID
localparam TM_PRE_VALID = 2'b01;
localparam TM_VALID = 2'b10;
reg [1:0] tm_valid_r;
wire tm_valid = tm_valid_r[1];
wire tm_pre_valid = tm_valid_r[0];
wire tile_valid = |t_tnum || (t_sel ? t0z_en : t1z_en);
wire tsr_allowed = tiles && tsr_rdy;
wire tile_good = tm_valid && tile_valid;
wire tile_wait = tile_good && !tsr_allowed;
wire tile_skip = tm_valid && !tile_valid;
wire tile_go = tile_good && tsr_allowed;
always @(posedge clk)
if (t_layer_start || tile_wait)
tm_valid_r <= TM_PRE_VALID;
else if (tm_pre_valid || tile_go)
tm_valid_r <= TM_VALID;
always @(posedge clk)
if (t_layer_start)
tx <= 6'd0;
else if (tm_pre_valid || tile_skip || tile_go)
tx <= tx + 6'd1;
else if (tile_wait)
tx <= tx - 6'd1;
// tile Y geometry
assign t_line = line[4:0] + ty_offs;
// --- Sprites ---
// sprite descriptor fields
// R0
wire [15:0] sfile_rdata;
wire [8:0] s_ycrd = sfile_rdata[8:0];
wire [2:0] s_ysz = sfile_rdata[11:9];
wire s_act = sfile_rdata[13];
wire s_leap = sfile_rdata[14];
wire s_yflp = sfile_rdata[15];
// R1
wire [8:0] s_xcrd = sfile_rdata[8:0];
wire [2:0] s_xsz = sfile_rdata[11:9];
wire s_xflp = sfile_rdata[15];
// R2
wire [11:0] s_tnum = sfile_rdata[11:0];
wire [3:0] s_pal = sfile_rdata[15:12];
// TSR control
wire [5:0] sprites_addr = s_tnum[5:0];
// internal layers control
reg sprites_last_r;
reg [2:0] s_layer;
reg [7:0] sreg;
reg [4:0] sr_valid;
reg s_leap_r;
wire sr0_pre_valid = sr_valid[0];
wire sr0_valid = sr_valid[1];
wire sr1_pre_valid = sr_valid[2];
wire sr1_valid = sr_valid[3];
wire sr2_valid = sr_valid[4];
wire sprite_go;
wire s_visible;
wire spr_valid = s_visible && s_act;
wire s_layer_end = (sr0_valid && !spr_valid && s_leap) || (sprite_go && s_leap_r);
wire sprites_last = sr0_valid && sprites_last_r;
assign spr_end = ({3{s_layer_end}} & s_layer[2:0]) | {3{sprites_last}};
always @(posedge clk)
sprites_last_r <= sreg == 8'd255;
always @(posedge clk)
if (start)
s_layer <= 3'b1;
else if (s_layer_end)
s_layer <= {s_layer[1:0], 1'b0};
// SFile registers control
// condition write to sreg write to sr_valid action
// start 0 SR0_PRE_VALID Start
// sr0_pre_valid sreg+3 SR0_VALID SR0 pre-read
// sr0_valid && !spr_valid sreg+3 - Skip sprite
// sr0_valid && spr_valid sreg-2 SR1_PRE_VALID SR1 pre-read
// sr1_pre_valid sreg+1 SR1_VALID SR1 read
// sr1_valid sreg+1 SR2_VALID SR2 pre-read
// sr2_valid && !tsr_rdy - - Wait for TSR ready
// sr2_valid && tsr_rdy sreg+1 SR0_PRE_VALID Next sprite
// sprites_last - NONE_VALID End
localparam NONE_VALID = 5'b00000;
localparam SR0_PRE_VALID = 5'b00001;
localparam SR0_VALID = 5'b00010;
localparam SR1_PRE_VALID = 5'b00100;
localparam SR1_VALID = 5'b01000;
localparam SR2_VALID = 5'b10000;
assign sprite_go = sr2_valid && sprites && tsr_rdy; // a kick to renderer
always @(posedge clk)
if (start)
sr_valid <= SR0_PRE_VALID;
else if (sprites_last)
sr_valid <= NONE_VALID;
else if (sr0_pre_valid)
sr_valid <= SR0_VALID;
else if (sr0_valid && spr_valid)
sr_valid <= SR1_PRE_VALID;
else if (sr1_pre_valid)
sr_valid <= SR1_VALID;
else if (sr1_valid)
sr_valid <= SR2_VALID;
else if (sprite_go)
sr_valid <= SR0_PRE_VALID;
always @(posedge clk)
if (start)
sreg <= 8'd0;
else if (sr0_pre_valid)
sreg <= sreg + 8'd3;
else if (sr0_valid)
sreg <= spr_valid ? (sreg - 8'd2) : (sreg + 8'd3);
else if (sr1_pre_valid || sprite_go)
sreg <= sreg + 8'd1;
// sprite Y geometry
reg [5:0] s_bmline_offset_r;
wire [8:0] s_line = line - s_ycrd; // visible line of sprite in current video line
wire [5:0] s_ymax = {s_ysz, 3'b111};
assign s_visible = (s_line <= s_ymax); // check if sprite line is within Y size
wire [8:0] sprites_line = {s_tnum[11:6], 3'b0} + s_bmline_offset_r;
wire [5:0] s_bmline_offset = s_yflp ? (s_ymax - s_line[5:0]) : s_line[5:0];
// SFile control
reg [8:0] sprites_x;
reg [2:0] sprites_xs;
reg sprites_xf;
always @(posedge clk)
begin
if (sr0_valid)
begin
s_leap_r <= s_leap;
s_bmline_offset_r <= s_bmline_offset;
end
if (sr1_valid)
begin
sprites_x <= s_xcrd;
sprites_xs <= s_xsz;
sprites_xf <= s_xflp;
end
end
// SFile
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) video_sfile
(
.clock (clk),
.address_a (sfile_addr_in),
.data_a (sfile_data_in),
.wren_a (sfile_we),
.address_b (sreg),
.q_b (sfile_rdata)
);
/*
altdpram video_sfile
(
.outclock (clk),
.wren (sfile_we),
.inclock (clk),
.data (sfile_data_in),
.rdaddress (sreg),
.wraddress (sfile_addr_in),
.q (sfile_rdata),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_sfile.indata_aclr = "OFF",
video_sfile.indata_reg = "INCLOCK",
video_sfile.intended_device_family = "ACEX1K",
video_sfile.lpm_type = "altdpram",
video_sfile.outdata_aclr = "OFF",
video_sfile.outdata_reg = "OUTCLOCK",
video_sfile.rdaddress_aclr = "OFF",
video_sfile.rdaddress_reg = "UNREGISTERED",
video_sfile.rdcontrol_aclr = "OFF",
video_sfile.rdcontrol_reg = "UNREGISTERED",
video_sfile.width = 16,
video_sfile.widthad = 8,
video_sfile.wraddress_aclr = "OFF",
video_sfile.wraddress_reg = "INCLOCK",
video_sfile.wrcontrol_aclr = "OFF",
video_sfile.wrcontrol_reg = "INCLOCK";
*/
// 4 buffers * 2 tile-planes * 64 tiles * 16 bits (9x16) - used to prefetch tiles
// (2 altdprams)
dpram #(.DATAWIDTH(16), .ADDRWIDTH(9)) video_tmbuf
(
.clock (clk),
.address_a (tmb_waddr),
.data_a (dram_rdata),
.wren_a (tm_next),
.address_b (tmb_raddr),
.q_b (tmb_rdata)
);
/*
altdpram video_tmbuf
(
.outclock (clk),
.wren (tm_next),
.inclock (clk),
.data (dram_rdata),
.rdaddress (tmb_raddr),
.wraddress (tmb_waddr),
.q (tmb_rdata),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_tmbuf.indata_aclr = "OFF",
video_tmbuf.indata_reg = "INCLOCK",
video_tmbuf.intended_device_family = "ACEX1K",
video_tmbuf.lpm_type = "altdpram",
video_tmbuf.outdata_aclr = "OFF",
video_tmbuf.outdata_reg = "OUTCLOCK",
video_tmbuf.rdaddress_aclr = "OFF",
video_tmbuf.rdaddress_reg = "UNREGISTERED",
video_tmbuf.rdcontrol_aclr = "OFF",
video_tmbuf.rdcontrol_reg = "UNREGISTERED",
video_tmbuf.width = 16,
video_tmbuf.widthad = 9,
video_tmbuf.wraddress_aclr = "OFF",
video_tmbuf.wraddress_reg = "INCLOCK",
video_tmbuf.wrcontrol_aclr = "OFF",
video_tmbuf.wrcontrol_reg = "INCLOCK";
*/
// TS renderer interface
assign tsr_go = sprite_go || tile_go;
assign tsr_x = sprites ? sprites_x : tile_x;
assign tsr_xs = sprites ? sprites_xs : 3'd0;
assign tsr_xf = sprites ? sprites_xf : t_xflp;
assign tsr_page = sprites ? sgpage : tile_page;
assign tsr_line = sprites ? sprites_line : tile_line;
assign tsr_addr = sprites ? sprites_addr : tile_addr;
assign tsr_pal = sprites ? s_pal : tile_pal;
endmodule

View File

@ -1,150 +1,150 @@
`include "tune.v"
// This module renders pixels into TS-line for tiles/sprites
// Task execution is initiated by 'tsr_go' (one 'clk' period strobe). // This module renders pixels into TS-line for tiles/sprites
// Inputs (only valid by 'tsr_go'): // Task execution is initiated by 'tsr_go' (one 'clk' period strobe).
// - DRAM address of graphics data (including page, line, word) // Inputs (only valid by 'tsr_go'):
// - number of cycles to render (one cycle is 2 words = 8 pixels; 8 cycles = 64 pixels max) // - DRAM address of graphics data (including page, line, word)
// - X coordinate // - number of cycles to render (one cycle is 2 words = 8 pixels; 8 cycles = 64 pixels max)
// - X flip bit // - X coordinate
// - palette selector // - X flip bit
// Address in TS-line is calculated from X coordinate respecting the X flip. // - palette selector
// Inc/dec direction is set automatically. // Address in TS-line is calculated from X coordinate respecting the X flip.
// At the 'c2' of last DRAM cycle 'mem_rdy' is asserted. // Inc/dec direction is set automatically.
// It should be used in comb to generate next cycle 'tsr_go' strobe for continuous renderer operation. // At the 'c2' of last DRAM cycle 'mem_rdy' is asserted.
// First 'tsr_go' may be issued at any DRAM cycle, but the operation starts only // It should be used in comb to generate next cycle 'tsr_go' strobe for continuous renderer operation.
// after TS request recognized and processed by DRAM controller. // First 'tsr_go' may be issued at any DRAM cycle, but the operation starts only
// It is recommended to assert 'tsr_go' at 'c2'. // after TS request recognized and processed by DRAM controller.
// It is recommended to assert 'tsr_go' at 'c2'.
module video_ts_render module video_ts_render
( (
// clocks // clocks
input wire clk, input wire clk,
// controls // controls
input wire reset, // line start strobe, inits the machine input wire reset, // line start strobe, inits the machine
input wire [ 8:0] x_coord, // address in TS-line where render to, auto-flipped to match 'flip' times 'x_size' input wire [ 8:0] x_coord, // address in TS-line where render to, auto-flipped to match 'flip' times 'x_size'
input wire [ 2:0] x_size, // number of rendering cycles (each cycle is 8 pixels, 0 = 1 cycle) input wire [ 2:0] x_size, // number of rendering cycles (each cycle is 8 pixels, 0 = 1 cycle)
input wire flip, // indicates that sprite is X-flipped input wire flip, // indicates that sprite is X-flipped
input wire tsr_go, // 1 clk 28MHz strobe for render start. Should be issued along with 'mem_rdy' for continuous process input wire tsr_go, // 1 clk 28MHz strobe for render start. Should be issued along with 'mem_rdy' for continuous process
input wire [ 5:0] addr, // address of dword within the line (dword = 8 pix) input wire [ 5:0] addr, // address of dword within the line (dword = 8 pix)
input wire [ 8:0] line, // line of bitmap input wire [ 8:0] line, // line of bitmap
input wire [ 7:0] page, // 1st page of bitmap (TxGpage or SGpage) input wire [ 7:0] page, // 1st page of bitmap (TxGpage or SGpage)
input wire [ 3:0] pal, // palette selector, bits[7:4] of CRAM address input wire [ 3:0] pal, // palette selector, bits[7:4] of CRAM address
output wire mem_rdy, // ready to receive new task output wire mem_rdy, // ready to receive new task
// TS-Line interface // TS-Line interface
output reg [ 8:0] ts_waddr, output reg [ 8:0] ts_waddr,
output wire [ 7:0] ts_wdata, output wire [ 7:0] ts_wdata,
output wire ts_we, output wire ts_we,
// DRAM interface // DRAM interface
output wire [20:0] dram_addr, output wire [20:0] dram_addr,
output wire dram_req, output wire dram_req,
input wire [15:0] dram_rdata, input wire [15:0] dram_rdata,
input wire dram_pre_next, input wire dram_pre_next,
input wire dram_next input wire dram_next
); );
// renderer mux
// DRAM request reg [15:0] data;
assign dram_req = tsr_go || !mem_rdy; reg [3:0] pal_r;
reg [2:0] pix_cnt;
// DRAM addressing wire [3:0] pix_m[0:3];
assign dram_addr = tsr_go ? addr_in : addr_next; wire [3:0] pix = pix_m[pix_cnt[1:0]];
wire [20:0] addr_in = {addr_offset, addr, 1'b0}; assign ts_wdata = {pal_r, pix};
wire [13:0] addr_offset = {page[7:3], line};
wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next}; assign pix_m[0] = data[7:4];
// as renderer can't move outside the single bitmap line, only 7 bits are processed assign pix_m[1] = data[3:0];
assign pix_m[2] = data[15:12];
reg [20:0] addr_reg; assign pix_m[3] = data[11:8];
always @(posedge clk) addr_reg <= dram_addr;
// DRAM request
assign dram_req = tsr_go || !mem_rdy;
// DRAM cycles counter
assign mem_rdy = cyc[4]; // DRAM addressing
reg [20:0] addr_reg;
reg [4:0] cyc;
always @(posedge clk) wire [13:0] addr_offset = {page[7:3], line};
if (reset) wire [20:0] addr_in = {addr_offset, addr, 1'b0};
cyc <= 5'b10000; wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
else if (tsr_go) assign dram_addr = tsr_go ? addr_in : addr_next;
cyc <= {1'b0, x_size, 1'b1};
else if (dram_pre_next) always @(posedge clk)
cyc <= cyc - 5'd1; addr_reg <= dram_addr;
// DRAM cycles counter
// DRAM data fetching reg [4:0] cyc;
reg [15:0] data;
always @(posedge clk) if (dram_next) data <= dram_rdata; assign mem_rdy = cyc[4];
always @(posedge clk or posedge reset)
// pixel render counter if (reset)
assign ts_we = render_on && |pix; // write signal for TS-line cyc <= 5'b10000;
wire render_on = !cnt[2]; else if (tsr_go)
cyc <= {1'b0, x_size, 1'b1};
reg [2:0] cnt; else if (dram_pre_next)
always @(posedge clk) cyc <= cyc - 5'd1;
if (reset)
cnt <= 3'b100; // DRAM data fetching
else if (dram_next) always @(posedge clk)
cnt <= 3'b000; if (dram_next)
else if (render_on) data <= dram_rdata;
cnt <= cnt + 3'd1;
// pixel render counter
wire render_on = !pix_cnt[2];
// renderer reload assign ts_we = render_on && |pix; // write signal for TS-line
reg tsr_rld;
always @(posedge clk) always @(posedge clk or posedge reset)
if (reset) if (reset)
tsr_rld <= 1'b0; pix_cnt <= 3'b100;
else if (tsr_go) else if (dram_next)
tsr_rld <= 1'b1; pix_cnt <= 3'b000;
else if (dram_next) else if (render_on)
tsr_rld <= 1'b0; pix_cnt <= pix_cnt + 3'd1;
// renderer reload
// delayed values reg tsr_rld;
reg [8:0] x_coord_d; always @(posedge clk or posedge reset)
reg [3:0] pal_d; if (reset)
reg flip_d; tsr_rld <= 1'b0;
always @(posedge clk) else if (tsr_go)
if (tsr_go) tsr_rld <= 1'b1;
begin else if (dram_next)
x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0); tsr_rld <= 1'b0;
pal_d <= pal;
flip_d <= flip; // delayed values
end reg [8:0] x_coord_d;
reg [3:0] pal_d;
reg flip_d;
// TS-line address
wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr); always @(posedge clk)
wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1}; if (tsr_go)
wire tsr_rld_stb = tsr_rld && dram_next; begin
x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0);
always @(posedge clk) ts_waddr <= ts_waddr_mx; pal_d <= pal;
flip_d <= flip;
reg [3:0] pal_r; end
reg flip_r;
always @(posedge clk)
if (tsr_rld_stb) // TS-line address
begin reg flip_r;
pal_r <= pal_d;
flip_r <= flip_d; wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
end wire tsr_rld_stb = tsr_rld && dram_next;
wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr);
// renderer mux always @(posedge clk)
assign ts_wdata = {pal_r, pix}; ts_waddr <= ts_waddr_mx;
wire [3:0] pix = pix_m[cnt[1:0]];
always @(posedge clk)
wire [3:0] pix_m[0:3]; if (tsr_rld_stb)
assign pix_m[0] = data[7:4]; begin
assign pix_m[1] = data[3:0]; pal_r <= pal_d;
assign pix_m[2] = data[15:12]; flip_r <= flip_d;
assign pix_m[3] = data[11:8]; end
endmodule
endmodule

140
rtl/z80/zclock.v Normal file
View File

@ -0,0 +1,140 @@
// PentEvo project (c) NedoPC 2008-2011
//
// Z80 clocking module, also contains some wait-stating when 14MHz
//
// IDEAL:
// clk _/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zclk /```\___/```\___/```\___/```````\_______/```````\_______/```````````````\_______________/```````````````\_______________/`
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zpos `\___/```\___/```\___/```\___________/```\___________/```\___________________________/```\___________________________/```\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zneg _/```\___/```\___/```\_______/```\___________/```\___________________/```\___________________________/```\________________
// clock phasing:
// c3 must be zpos for 7mhz, therefore c1 - zneg
// for 3.5 mhz, c3 is both zpos and zneg (alternating)
// 14MHz rulez:
// 1. do variable stalls for memory access.
// 2. do fallback on 7mhz for external IO accesses
// 3. clock switch 14-7-3.5 only at RFSH
`include "tune.v"
module zclock
(
input clk,
output reg zclk_out = 0, // generated Z80 clock, passed through inverter externally
input c0, c2,
input wire iorq_s,
input wire external_port,
output reg zpos,
output reg zneg,
// stall enables and triggers
input wire cpu_stall,
input wire ide_stall,
input wire dos_on,
input wire vdos_off,
`ifdef PENT_312
input wire boost_start,
input wire [4:0] hcnt,
input wire upper8,
`endif
input [1:0] turbo // 2'b00 - 3.5 MHz
// 2'b01 - 7.0 MHz
// 2'b1x - 14.0 MHz
);
`ifdef PENT_312
// Turbo-boost for Pentagon 71680 tacts emulation with 312 video lines
wire [1:0] turbo_int = (turbo == 2'b00) ? (t_boost ? 2'b01 : 2'b00) : turbo;
reg t_boost = 0;
reg [4:0] hcnt_r = 0;
always @(posedge clk)
if (boost_start && !t_boost)
begin
t_boost <= 1'b1;
hcnt_r <= hcnt;
end
else if (t_boost && !upper8 && (hcnt_r == hcnt))
t_boost <= 1'b0;
`else
wire [1:0] turbo_int = turbo;
`endif
// wait generator
reg [3:0] stall_count = 0;
wire dos_stall = dos_on || vdos_off;
wire io_stall = iorq_s && external_port && turbo_int[1];
wire stall_start = dos_stall || io_stall;
wire stall_count_end = stall_count[3];
wire dos_io_stall = stall_start || !stall_count_end;
always @(posedge clk)
if (stall_start)
begin
if (dos_stall)
stall_count <= 4'd4; // 4 tacts 28MHz (1 tact 7MHz)
else if (io_stall)
stall_count <= 4'd0; // 8 tacts 28MHz (1 tact 3.5MHz)
end
else if (!stall_count_end)
stall_count <= stall_count + 3'd1;
// Z80 clocking pre-strobes
reg clk14_src = 0; // source for 14MHz clock
reg c2_cnt = 0;
wire stall = cpu_stall || dos_io_stall || ide_stall;
wire pre_zpos_140 = clk14_src;
wire pre_zneg_140 = ~clk14_src;
wire pre_zpos_70 = c2;
wire pre_zneg_70 = c0;
wire pre_zpos_35 = c2_cnt && c2;
wire pre_zneg_35 = !c2_cnt && c2;
wire pre_zpos = turbo_int[1] ? pre_zpos_140 : (turbo_int[0] ? pre_zpos_70 : pre_zpos_35);
wire pre_zneg = turbo_int[1] ? pre_zneg_140 : (turbo_int[0] ? pre_zneg_70 : pre_zneg_35);
always @(posedge clk)
if (!stall)
clk14_src <= ~clk14_src;
always @(posedge clk) if (c2)
c2_cnt <= ~c2_cnt;
// Z80 clocking strobes
always @(posedge clk)
begin
zpos <= !stall && pre_zpos && zclk_out;
zneg <= !stall && pre_zneg && !zclk_out;
end
// make Z80 clock: account for external inversion and make some leading of clock
// 9.5 ns propagation delay: from clk posedge to zclk returned back any edge
// (1/28)/2=17.9ns half a clock lead
// 2.6ns lag because of non-output register emitting of zclk_out
// total: 5.8 ns lead of any edge of zclk relative to posedge of clk => ACCOUNT FOR THIS WHEN DOING INTER-CLOCK DATA TRANSFERS
// Z80 clocking
always @(negedge clk)
begin
if (zpos)
zclk_out <= 1'b0;
if (zneg)
zclk_out <= 1'b1;
end
endmodule

136
rtl/z80/zint.v Normal file
View File

@ -0,0 +1,136 @@
`include "tune.v"
module zint
(
input wire clk,
input wire zpos,
input wire res,
input wire wait_n,
input wire int_start_frm,
input wire int_start_lin,
input wire int_start_dma,
input wire int_start_wtp,
input wire vdos, // pre_vdos
input wire intack,
`ifdef PENT_312
output wire boost_start,
`endif
input wire [7:0] intmask,
output wire [7:0] im2vect,
output wire int_n
);
// In VDOS INTs are focibly disabled.
// For Frame, Line INT its generation is blocked, it will be lost.
// For DMA INT only its output is blocked, so DMA ISR will will be processed as soon as returned from VDOS.
reg [1:0] int_sel = 0;
reg int_frm;
reg int_dma;
reg int_lin;
reg int_wtp;
reg intack_r;
wire intctr_fin;
localparam INTFRM = 2'b00;
localparam INTLIN = 2'b01;
localparam INTDMA = 2'b10;
localparam INTWTP = 2'b11;
wire [7:0] vect [0:3];
assign vect[INTFRM] = 8'hFF;
assign vect[INTLIN] = 8'hFD;
assign vect[INTDMA] = 8'hFB;
assign vect[INTWTP] = 8'hF9;
assign im2vect = {vect[int_sel]};
wire int_all = (int_frm || int_lin || int_dma || int_wtp) && !vdos;
assign int_n = int_all ? 1'b0 : 1'b1;
wire dis_int_frm = !intmask[0];
wire dis_int_lin = !intmask[1];
wire dis_int_dma = !intmask[2];
wire dis_int_wtp = !intmask[3];
`ifdef PENT_312
assign boost_start = intack_s || intctr_fin_s;
wire intctr_fin_s = !intctr_fin_r && intctr_fin;
reg intctr_fin_r;
always @(posedge clk)
intctr_fin_r <= intctr_fin;
`endif
wire intack_s = intack && !intack_r;
always @(posedge clk)
intack_r <= intack;
// ~INT source latch
always @(posedge clk)
if (intack_s)
begin
if (int_frm)
int_sel <= INTFRM; // priority 0
else if (int_lin)
int_sel <= INTLIN; // priority 1
else if (int_dma)
int_sel <= INTDMA; // priority 2
else if (int_wtp)
int_sel <= INTWTP; // priority 3
end
// ~INT generating
always @(posedge clk)
if (res || dis_int_frm)
int_frm <= 1'b0;
else if (int_start_frm)
int_frm <= 1'b1;
else if (intack_s || intctr_fin) // priority 0
int_frm <= 1'b0;
always @(posedge clk)
if (res || dis_int_lin)
int_lin <= 1'b0;
else if (int_start_lin)
int_lin <= 1'b1;
else if (intack_s && !int_frm) // priority 1
int_lin <= 1'b0;
always @(posedge clk)
if (res || dis_int_dma)
int_dma <= 1'b0;
else if (int_start_dma)
int_dma <= 1'b1;
else if (intack_s && !int_frm && !int_lin) // priority 2
int_dma <= 1'b0;
always @(posedge clk)
if (res || dis_int_wtp)
int_wtp <= 1'b0;
else if (int_start_wtp)
int_wtp <= 1'b1;
else if (intack_s && !int_frm && !int_lin && !int_dma) // priority 3
int_wtp <= 1'b0;
// ~WAIT resync
reg wait_r;
always @(posedge clk)
wait_r <= !wait_n;
// ~INT counter
reg [5:0] intctr;
assign intctr_fin = intctr[5]; // 32 clks
always @(posedge clk, posedge int_start_frm)
begin
if (int_start_frm)
intctr <= 1'b0;
else if (zpos && !intctr_fin && !wait_r && !vdos)
intctr <= intctr + 1'b1;
end
endmodule

62
rtl/z80/zmaps.v Normal file
View File

@ -0,0 +1,62 @@
// This module maps z80 memory accesses into FPGA RAM and ports
`include "tune.v"
module zmaps
(
// Z80 controls
input wire clk,
input wire memwr_s,
input wire [15:0] a,
input wire [7:0] d,
// config data
input wire [4:0] fmaddr,
// FPRAM data
output wire [15:0] zmd,
output wire [7:0] zma,
// DMA
input wire [15:0] dma_data,
input wire [7:0] dma_wraddr,
input wire dma_cram_we,
input wire dma_sfile_we,
// write strobes
output wire cram_we,
output wire sfile_we,
output wire regs_we
);
// addresses of files withing zmaps
localparam CRAM = 3'b000;
localparam SFIL = 3'b001;
localparam REGS = 4'b0100;
// DMA
wire dma_req = dma_cram_we || dma_sfile_we;
// control signals
wire hit = (a[15:12] == fmaddr[3:0]) && fmaddr[4] && memwr_s;
wire cram_hit = (a[11:9] == CRAM) && hit;
wire sfile_hit = (a[11:9] == SFIL) && hit;
// write enables
wire lower_byte_we = (cram_hit || sfile_hit) && !a[0];
assign cram_we = dma_req ? dma_cram_we : cram_hit && a[0];
assign sfile_we = dma_req ? dma_sfile_we : sfile_hit && a[0];
assign regs_we = (a[11:8] == REGS) && hit;
// LSB fetching
reg [7:0] lower_byte;
assign zma = dma_req ? dma_wraddr : a[8:1];
assign zmd = dma_req ? dma_data : {d, lower_byte};
always @(posedge clk)
if (lower_byte_we)
lower_byte <= d;
endmodule

321
rtl/z80/zmem.v Normal file
View File

@ -0,0 +1,321 @@
`include "tune.v"
// PentEvo project (c) NedoPC 2008-2009
//
// Z80 memory manager: routes ROM/RAM accesses, makes wait-states for 14MHz or stall condition, etc.
//
//
// clk _/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\_/`\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zclk /```\___/```\___/```\___/```````\_______/```````\_______/```````````````\_______________/```````````````\_______________/`
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zpos `\___/```\___/```\___/```\___________/```\___________/```\___________________________/```\___________________________/```\
// | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
// zneg _/```\___/```\___/```\_______/```\___________/```\___________________/```\___________________________/```\________________
module zmem
(
input wire clk,
input wire c1, c2, c3,
input wire zneg, // strobes which show positive and negative edges of zclk
// Z80
input wire rst,
input wire [15:0] za,
output wire [ 7:0] zd_out, // output to Z80 bus
output wire zd_ena, // output to Z80 bus enable
input wire opfetch,
input wire opfetch_s,
input wire memrd,
input wire memwr,
input wire memwr_s,
input wire [ 1:0] turbo, // 2'b00 - 3.5,
// 2'b01 - 7.0,
// 2'b1x - 14.0
input wire [3:0] cache_en,
input wire [3:0] memconf,
input wire [31:0] xt_page,
output wire [4:0] rompg,
output wire csrom,
output wire romoe_n,
output wire romwe_n,
output wire dos,
output wire dos_on,
output wire dos_off,
output wire vdos,
output reg pre_vdos,
input wire vdos_on,
input wire vdos_off,
// DRAM
output wire cpu_req,
output wire [20:0] cpu_addr,
output wire cpu_wrbsel,
input wire [15:0] cpu_rddata,
input wire cpu_next,
input wire cpu_strobe,
input wire cpu_latch,
output wire cpu_stall // for zclock
);
// controls
wire rom128 = memconf[0];
wire w0_we = memconf[1];
wire w0_map_n = memconf[2];
wire w0_ram = memconf[3];
// pager
wire [7:0] xtpage[0:3];
assign xtpage[0] = vdos ? 8'hFF : {xt_page[7:2], w0_map_n ? xt_page[1:0] : {~dos, rom128}};
assign xtpage[1] = xt_page[15:8];
assign xtpage[2] = xt_page[23:16];
assign xtpage[3] = xt_page[31:24];
wire [1:0] win = za[15:14];
wire win0 = ~|win;
wire ramwr_en = !win0 || w0_we || vdos;
wire rom_n_ram = win0 && !w0_ram && !vdos;
wire [7:0] page = xtpage[win];
// DOS signal control
reg dos_r;
assign dos_on = win0 && opfetch_s && (za[13:8]==6'h3D) && rom128 && !w0_map_n;
assign dos_off = !win0 && opfetch_s && !vdos;
assign dos = (dos_on || dos_off) ^^ dos_r; // to make dos appear 1 clock earlier than dos_r
always @(posedge clk)
if (rst)
dos_r <= 1'b0;
else if (dos_off)
dos_r <= 1'b0;
else if (dos_on)
dos_r <= 1'b1;
// VDOS signal control
// vdos switching is delayed till next opfetch due to INIR that writes right after iord cycle
reg vdos_r;
assign vdos = opfetch ? pre_vdos : vdos_r; // vdos appears as soon as first opfetch
always @(posedge clk)
if (rst || vdos_off)
begin
pre_vdos <= 1'b0;
vdos_r <= 1'b0;
end
else if (vdos_on)
pre_vdos <= 1'b1;
else if (opfetch_s)
vdos_r <= pre_vdos;
// RAM
wire cache_hit_en;
assign zd_ena = !rom_n_ram && memrd;
wire ramreq = ((memrd && !cache_hit_en) || (memwr && ramwr_en));
// address, data in and data out
wire [15:0] cache_d;
wire cache_v;
assign cpu_wrbsel = za[0];
assign cpu_addr[20:0] = {page, za[13:1]};
wire [15:0] mem_d = cpu_latch ? cpu_rddata : cache_d;
assign zd_out = ~cpu_wrbsel ? mem_d[7:0] : mem_d[15:8];
// Z80 controls
// 7/3.5MHz support
reg ramreq_r;
reg pre_ramreq_r;
reg pending_cpu_req;
reg stall14_cycrd;
reg stall14_fin;
wire dram_beg = ramreq && !pre_ramreq_r && zneg;
wire cpureq_357 = ramreq && !ramreq_r;
wire cpureq_14 = dram_beg || pending_cpu_req;
wire stall357 = cpureq_357 && !cpu_next;
wire stall14_ini = dram_beg && (!cpu_next || opfetch || memrd); // no wait at all in write cycles, if next dram cycle is available
wire stall14_cyc = memrd ? stall14_cycrd : !cpu_next;
wire stall14 = stall14_ini || stall14_cyc || stall14_fin;
wire turbo14 = turbo[1];
assign cpu_req = turbo14 ? cpureq_14 : cpureq_357;
assign cpu_stall = turbo14 ? stall14 : stall357;
// 14MHz support
// wait tables:
//
// M1 opcode fetch, dram_beg concurs with:
// c3: +3
// c2: +4
// c1: +5
// c0: +6
//
// memory read, dram_beg concurs with:
// c3: +2
// c2: +3
// c1: +4
// c0: +5
//
// memory write: no wait
//
// special case: if dram_beg pulses 1 when cpu_next is 0,
// unconditional wait has to be performed until cpu_next is 1, and
// then wait as if dram_beg would concur with c0
// memrd, opfetch - wait till c3 && cpu_next,
// memwr - wait till cpu_next
always @(posedge clk)
if (c3 && !cpu_stall)
ramreq_r <= ramreq;
always @(posedge clk)
if (zneg)
pre_ramreq_r <= ramreq;
always @(posedge clk)
if (rst)
pending_cpu_req <= 1'b0;
else if (cpu_next && c3)
pending_cpu_req <= 1'b0;
else if (dram_beg)
pending_cpu_req <= 1'b1;
always @(posedge clk)
if (rst)
stall14_cycrd <= 1'b0;
else if (cpu_next && c3)
stall14_cycrd <= 1'b0;
else if (dram_beg && (!c3 || !cpu_next) && (opfetch || memrd))
stall14_cycrd <= 1'b1;
always @(posedge clk)
if (rst)
stall14_fin <= 1'b0;
else if (stall14_fin && ((opfetch && c1) || (memrd && c2)))
stall14_fin <= 1'b0;
else if (cpu_next && c3 && cpu_req && (opfetch || memrd))
stall14_fin <= 1'b1;
// cache
wire [12:0] cache_a;
wire [12:0] cpu_hi_addr = {page[7:0], za[13:9]};
// wire cache_hit = (ch_addr[7:2] != 6'b011100) && (cpu_hi_addr == cache_a) && cache_v; // debug for BM
wire cache_hit = (cpu_hi_addr == cache_a) && cache_v; // asynchronous signal meaning that address requested by CPU is cached and valid
assign cache_hit_en = cache_hit && cache_en[win];
wire cache_inv = cache_hit && memwr_s && ramwr_en; // cache invalidation should be only performed if write happens to cached address
wire [7:0] ch_addr = cpu_addr[7:0];
// wire [14:0] cpu_hi_addr = {page[7:0], za[13:7]};
// wire [14:0] cache_a;
// wire [7:0] ch_addr = {2'b0, cpu_addr[5:0]};
dpram #(.DATAWIDTH(16), .ADDRWIDTH(8)) cache_data
(
.clock(clk),
.address_a(ch_addr),
.data_a(cpu_rddata),
.wren_a(cpu_strobe),
.address_b(ch_addr),
.q_b(cache_d)
);
dpram #(.DATAWIDTH(14), .ADDRWIDTH(8)) cache_addr
(
.clock(clk),
.address_a(ch_addr),
.data_a({!cache_inv, cpu_hi_addr}),
.wren_a(cpu_strobe || cache_inv),
.address_b(ch_addr),
.q_b({cache_v, cache_a})
);
/*
altdpram cache_data
(
.inclock (clk),
.outclock (clk),
.data (cpu_rddata),
.rdaddress (ch_addr),
.wraddress (ch_addr),
.wren (cpu_strobe),
.q (cache_d),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
cache_data.indata_aclr = "OFF",
cache_data.indata_reg = "INCLOCK",
cache_data.intended_device_family = "ACEX1K",
cache_data.lpm_type = "altdpram",
cache_data.outdata_aclr = "OFF",
cache_data.outdata_reg = "OUTCLOCK",
cache_data.rdaddress_aclr = "OFF",
cache_data.rdaddress_reg = "UNREGISTERED",
cache_data.rdcontrol_aclr = "OFF",
cache_data.rdcontrol_reg = "UNREGISTERED",
cache_data.width = 16,
cache_data.widthad = 8,
cache_data.wraddress_aclr = "OFF",
cache_data.wraddress_reg = "INCLOCK",
cache_data.wrcontrol_aclr = "OFF",
cache_data.wrcontrol_reg = "INCLOCK";
altdpram cache_addr
(
.inclock (clk),
.outclock (clk),
.data ({!cache_inv, cpu_hi_addr}),
.rdaddress (ch_addr),
.wraddress (ch_addr),
.wren (cpu_strobe || cache_inv),
.q ({cache_v, cache_a}),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
cache_addr.indata_aclr = "OFF",
cache_addr.indata_reg = "INCLOCK",
cache_addr.intended_device_family = "ACEX1K",
cache_addr.lpm_type = "altdpram",
cache_addr.outdata_aclr = "OFF",
cache_addr.outdata_reg = "OUTCLOCK",
cache_addr.rdaddress_aclr = "OFF",
cache_addr.rdaddress_reg = "UNREGISTERED",
cache_addr.rdcontrol_aclr = "OFF",
cache_addr.rdcontrol_reg = "UNREGISTERED",
cache_addr.width = 14,
cache_addr.widthad = 8,
cache_addr.wraddress_aclr = "OFF",
cache_addr.wraddress_reg = "INCLOCK",
cache_addr.wrcontrol_aclr = "OFF",
cache_addr.wrcontrol_reg = "INCLOCK";
*/
// ROM chip
assign csrom = rom_n_ram;
assign romoe_n = !memrd;
assign romwe_n = !(memwr && w0_we);
assign rompg = xtpage[0][4:0];
endmodule

746
rtl/z80/zports.v Normal file
View File

@ -0,0 +1,746 @@
// PentEvo project (c) NedoPC 2008-2010
`include "tune.v"
module zports
(
input wire zclk, // z80 clock
input wire clk,
input wire [ 7:0] din,
output reg [ 7:0] dout,
output wire dataout,
input wire [15:0] a,
input wire rst, // system reset
input wire opfetch,
input wire rd,
input wire wr,
input wire rdwr,
input wire iorq,
input wire iorq_s,
input wire iord,
input wire iord_s,
input wire iowr,
input wire iowr_s,
input wire iordwr,
input wire iordwr_s,
output wire porthit, // when internal port hit occurs, this is 1, else 0; used for iorq1_n iorq2_n on zxbus
output wire external_port, // asserts for AY and VG93 accesses
output wire zborder_wr,
output wire border_wr,
output wire zvpage_wr,
output wire vpage_wr,
output wire vconf_wr,
output wire gx_offsl_wr,
output wire gx_offsh_wr,
output wire gy_offsl_wr,
output wire gy_offsh_wr,
output wire t0x_offsl_wr,
output wire t0x_offsh_wr,
output wire t0y_offsl_wr,
output wire t0y_offsh_wr,
output wire t1x_offsl_wr,
output wire t1x_offsh_wr,
output wire t1y_offsl_wr,
output wire t1y_offsh_wr,
output wire tsconf_wr,
output wire palsel_wr,
output wire tmpage_wr,
output wire t0gpage_wr,
output wire t1gpage_wr,
output wire sgpage_wr,
output wire hint_beg_wr,
output wire vint_begl_wr,
output wire vint_begh_wr,
output wire [31:0] xt_page,
output reg [4:0] fmaddr,
input wire regs_we,
output reg [7:0] sysconf,
output reg [7:0] memconf,
output reg [3:0] cacheconf,
input wire cfg_floppy_swap,
output reg [7:0] fddvirt,
`ifdef FDR
output reg fdr_en,
output wire fdr_cnt_lat,
input wire [18:0] fdr_cnt,
output wire [9:0] dmaport_wr,
`else
output wire [8:0] dmaport_wr,
`endif
input wire dma_act,
output reg [1:0] dmawpdev,
output reg [7:0] intmask,
input wire dos,
input wire vdos,
output wire vdos_on,
output wire vdos_off,
output wire ay_bdir,
output wire ay_bc1,
output wire covox_wr,
output wire beeper_wr,
input wire tape_read,
`ifdef IDE_HDD
// IDE interface
input wire [15:0] ide_in,
output wire [15:0] ide_out,
output wire ide_cs0_n,
output wire ide_cs1_n,
output wire ide_req,
input wire ide_stb,
input wire ide_ready,
output reg ide_stall,
`endif
input wire [ 4:0] keys_in, // keys (port FE)
input wire [ 7:0] mus_in, // mouse (xxDF)
`ifdef KEMPSTON_8BIT
input wire [ 7:0] kj_in,
`else
input wire [ 4:0] kj_in,
`endif
input wire vg_intrq,
input wire vg_drq, // from vg93 module - drq + irq read
output wire vg_cs_n,
output wire vg_wrFF,
output wire [1:0] drive_sel, // disk drive selection
// SPI
output wire sdcs_n,
output wire sd2cs_n,
`ifdef IDE_VDAC2
output wire ftcs_n,
`ifdef ESP32_SPI
output wire espcs_n,
`endif
`endif
output reg spi_mode,
output wire sd_start,
output wire [ 7:0] sd_datain,
input wire [ 7:0] sd_dataout,
// WAIT-ports related
output reg [ 7:0] wait_addr,
output wire wait_start_gluclock, // begin wait from some ports
output wire wait_start_comport, //
output reg [ 7:0] wait_write,
input wire [ 7:0] wait_read
);
//#nnAF
localparam VCONF = 8'h00;
localparam VPAGE = 8'h01;
localparam GXOFFSL = 8'h02;
localparam GXOFFSH = 8'h03;
localparam GYOFFSL = 8'h04;
localparam GYOFFSH = 8'h05;
localparam TSCONF = 8'h06;
localparam PALSEL = 8'h07;
localparam XBORDER = 8'h0F;
localparam T0XOFFSL = 8'h40;
localparam T0XOFFSH = 8'h41;
localparam T0YOFFSL = 8'h42;
localparam T0YOFFSH = 8'h43;
localparam T1XOFFSL = 8'h44;
localparam T1XOFFSH = 8'h45;
localparam T1YOFFSL = 8'h46;
localparam T1YOFFSH = 8'h47;
localparam RAMPAGE = 8'h10; // this covers #10-#13
localparam FMADDR = 8'h15;
localparam TMPAGE = 8'h16;
localparam T0GPAGE = 8'h17;
localparam T1GPAGE = 8'h18;
localparam SGPAGE = 8'h19;
localparam DMASADDRL = 8'h1A;
localparam DMASADDRH = 8'h1B;
localparam DMASADDRX = 8'h1C;
localparam DMADADDRL = 8'h1D;
localparam DMADADDRH = 8'h1E;
localparam DMADADDRX = 8'h1F;
localparam SYSCONF = 8'h20;
localparam MEMCONF = 8'h21;
localparam HSINT = 8'h22;
localparam VSINTL = 8'h23;
localparam VSINTH = 8'h24;
localparam DMAWPD = 8'h25;
localparam DMALEN = 8'h26;
localparam DMACTRL = 8'h27;
localparam DMANUM = 8'h28;
localparam FDDVIRT = 8'h29;
localparam INTMASK = 8'h2A;
localparam CACHECONF = 8'h2B;
localparam DMAWPA = 8'h2D;
localparam XSTAT = 8'h00;
localparam DMASTAT = 8'h27;
`ifdef FDR
localparam DMANUMH = 8'h2C;
localparam FRCTRL = 8'h30;
localparam FRCNT0 = 8'h30;
localparam FRCNT1 = 8'h31;
localparam FRCNT2 = 8'h32;
`endif
`ifdef FDR
localparam FDR_VER = 1'b1;
`else
localparam FDR_VER = 1'b0;
`endif
`ifdef IDE_VDAC
localparam VDAC_VER = 3'h3;
`elsif IDE_VDAC2
localparam VDAC_VER = 3'h7;
`else
localparam VDAC_VER = 3'h0;
`endif
localparam PORTFE = 8'hFE;
localparam PORTFD = 8'hFD;
localparam PORTXT = 8'hAF;
localparam PORTF7 = 8'hF7;
localparam COVOX = 8'hFB;
`ifdef IDE_HDD
localparam NIDE10 = 8'h10;
localparam NIDE11 = 8'h11;
localparam NIDE30 = 8'h30;
localparam NIDE50 = 8'h50;
localparam NIDE70 = 8'h70;
localparam NIDE90 = 8'h90;
localparam NIDEB0 = 8'hB0;
localparam NIDED0 = 8'hD0;
localparam NIDEF0 = 8'hF0;
localparam NIDE08 = 8'h08;
localparam NIDE28 = 8'h28;
localparam NIDE48 = 8'h48;
localparam NIDE68 = 8'h68;
localparam NIDE88 = 8'h88;
localparam NIDEA8 = 8'hA8;
localparam NIDEC8 = 8'hC8;
localparam NIDEE8 = 8'hE8;
`endif
localparam VGCOM = 8'h1F;
localparam VGTRK = 8'h3F;
localparam VGSEC = 8'h5F;
localparam VGDAT = 8'h7F;
localparam VGSYS = 8'hFF;
localparam KJOY = 8'h1F;
localparam KMOUSE = 8'hDF;
localparam SDCFG = 8'h77;
localparam SDDAT = 8'h57;
localparam COMPORT = 8'hEF; // F8EF..FFEF - rs232 ports
reg [3:0] spi_cs_n;
reg pwr_up_reg;
reg [7:0] rampage[0:3];
wire gluclock_on;
wire p7ffd_wr;
assign sdcs_n = spi_cs_n[0];
assign sd2cs_n = spi_cs_n[2];
`ifdef IDE_VDAC2
assign ftcs_n = spi_cs_n[1];
`ifdef ESP32_SPI
assign espcs_n = spi_cs_n[3];
`endif
`endif
wire open_vg;
wire [7:0] loa = a[7:0];
wire [7:0] hoa = regs_we ? a[7:0] : a[15:8];
wire vg_port = (loa==VGCOM) || (loa==VGTRK) || (loa==VGSEC) || (loa==VGDAT);
wire vgsys_port = (loa==VGSYS);
assign porthit = ((loa==PORTFE) || (loa==PORTXT) || (loa==PORTFD) || (loa==COVOX)) || ((loa==PORTF7) && !dos)
`ifdef IDE_HDD
|| ide_all
`endif
|| ((vg_port || vgsys_port) && (dos || open_vg)) || ((loa==KJOY) && !dos && !open_vg) || (loa==KMOUSE) || ((loa==SDCFG) || (loa==SDDAT)) || (loa==COMPORT);
`ifdef IDE_HDD
wire ide_all = ide_even || ide_port11;
wire ide_even = (loa[2:0] == 3'b000) && (loa[3] != loa[4]); // even ports
wire ide_port11 = (loa==NIDE11); // 11
wire ide_port10 = (loa==NIDE10); // 10
wire ide_portc8 = (loa==NIDEC8); // C8
`endif
assign external_port = ((loa==PORTFD) && a[15]) // AY
|| (((loa==VGCOM) || (loa==VGTRK) || (loa==VGSEC) || (loa==VGDAT)) && (dos || open_vg));
assign dataout = porthit && iord && (~external_port);
// zclk-synchronous strobes
reg iowr_reg;
reg iord_reg;
reg port_wr;
reg port_rd;
always @(posedge zclk)
begin
iowr_reg <= iowr;
iord_reg <= iord;
if (!iowr_reg && iowr)
port_wr <= 1'b1;
else
port_wr <= 1'b0;
if (!iord_reg && iord)
port_rd <= 1'b1;
else
port_rd <= 1'b0;
end
// reading ports
always @*
begin
case (loa)
PORTFE:
dout = {1'b1, tape_read, 1'b1, keys_in};
`ifdef IDE_HDD
NIDE10,NIDE30,NIDE50,NIDE70,NIDE90,NIDEB0,NIDED0,NIDEF0,NIDE08,NIDE28,NIDE48,NIDE68,NIDE88,NIDEA8,NIDEC8,NIDEE8:
dout = iderdeven;
NIDE11:
dout = iderdodd;
`endif
PORTXT:
begin
case (hoa)
XSTAT:
dout = {1'b0, pwr_up_reg, FDR_VER, 2'b0, VDAC_VER};
DMASTAT:
dout = {dma_act, 7'b0};
RAMPAGE + 8'd2, RAMPAGE + 8'd3:
dout = rampage[hoa[1:0]];
`ifdef FDR
FRCNT0:
dout = fdr_cnt[7:0];
FRCNT1:
dout = fdr_cnt[15:8];
FRCNT2:
dout = {5'b0, fdr_cnt[18:16]};
`endif
default:
dout = 8'hFF;
endcase
end
VGSYS:
dout = {vg_intrq, vg_drq, 6'b111111};
KJOY:
`ifdef KEMPSTON_8BIT
dout = kj_in;
`else
dout = {3'b000, kj_in};
`endif
KMOUSE:
dout = mus_in;
SDCFG:
dout = 8'h00; // always SD inserted, SD is in R/W mode
SDDAT:
dout = sd_dataout;
PORTF7:
begin
if (!a[14] && (a[8] ^ dos) && gluclock_on) // $BFF7 - data i/o
dout = wait_read;
// dout = 8'h55;
else // any other $xxF7 port
dout = 8'hFF;
end
COMPORT:
begin
dout = wait_read; // $F8EF..$FFEF
end
default:
dout = 8'hFF;
endcase
end
// power-up
// This bit is loaded as 1 while FPGA is configured and automatically reset to 0 after STATUS port reading
reg pwr_up = 1'b1;
always @(posedge clk)
if (iord_s & (loa == PORTXT) & (hoa == XSTAT))
begin
pwr_up_reg <= pwr_up;
pwr_up <= 1'b0;
end
wire portfe_wr = (loa==PORTFE) && iowr_s;
assign beeper_wr = portfe_wr;
assign covox_wr = (loa==COVOX) && iowr_s;
wire portxt_wr = ((loa==PORTXT) && iowr_s) || regs_we;
wire portxt_rd = (loa==PORTXT) && iord_s;
assign xt_page = {rampage[3], rampage[2], rampage[1], rampage[0]};
// writing ports
assign dmaport_wr[0] = portxt_wr && (hoa == DMASADDRL);
assign dmaport_wr[1] = portxt_wr && (hoa == DMASADDRH);
assign dmaport_wr[2] = portxt_wr && (hoa == DMASADDRX);
assign dmaport_wr[3] = portxt_wr && (hoa == DMADADDRL);
assign dmaport_wr[4] = portxt_wr && (hoa == DMADADDRH);
assign dmaport_wr[5] = portxt_wr && (hoa == DMADADDRX);
assign dmaport_wr[6] = portxt_wr && (hoa == DMALEN);
assign dmaport_wr[7] = portxt_wr && (hoa == DMACTRL);
assign dmaport_wr[8] = portxt_wr && (hoa == DMANUM);
`ifdef FDR
assign dmaport_wr[9] = portxt_wr && (hoa == DMANUMH);
`endif
assign zborder_wr = portfe_wr;
assign border_wr = (portxt_wr && (hoa == XBORDER));
assign zvpage_wr = p7ffd_wr;
assign vpage_wr = (portxt_wr && (hoa == VPAGE ));
assign vconf_wr = (portxt_wr && (hoa == VCONF ));
assign gx_offsl_wr = (portxt_wr && (hoa == GXOFFSL));
assign gx_offsh_wr = (portxt_wr && (hoa == GXOFFSH));
assign gy_offsl_wr = (portxt_wr && (hoa == GYOFFSL));
assign gy_offsh_wr = (portxt_wr && (hoa == GYOFFSH));
assign t0x_offsl_wr = (portxt_wr && (hoa == T0XOFFSL));
assign t0x_offsh_wr = (portxt_wr && (hoa == T0XOFFSH));
assign t0y_offsl_wr = (portxt_wr && (hoa == T0YOFFSL));
assign t0y_offsh_wr = (portxt_wr && (hoa == T0YOFFSH));
assign t1x_offsl_wr = (portxt_wr && (hoa == T1XOFFSL));
assign t1x_offsh_wr = (portxt_wr && (hoa == T1XOFFSH));
assign t1y_offsl_wr = (portxt_wr && (hoa == T1YOFFSL));
assign t1y_offsh_wr = (portxt_wr && (hoa == T1YOFFSH));
assign tsconf_wr = (portxt_wr && (hoa == TSCONF));
assign palsel_wr = (portxt_wr && (hoa == PALSEL));
assign tmpage_wr = (portxt_wr && (hoa == TMPAGE));
assign t0gpage_wr = (portxt_wr && (hoa == T0GPAGE));
assign t1gpage_wr = (portxt_wr && (hoa == T1GPAGE));
assign sgpage_wr = (portxt_wr && (hoa == SGPAGE));
assign hint_beg_wr = (portxt_wr && (hoa == HSINT ));
assign vint_begl_wr = (portxt_wr && (hoa == VSINTL));
assign vint_begh_wr = (portxt_wr && (hoa == VSINTH));
`ifdef FDR
assign fdr_cnt_lat = (portxt_rd && (hoa == FRCNT0));
`endif
reg m1_lock128;
wire lock128_2 = memconf[7:6] == 2'b10; // mode 2
wire lock128_3 = memconf[7:6] == 2'b11; // mode 3
wire lock128 = lock128_3 ? 1'b0 : (lock128_2 ? m1_lock128 : memconf[6]);
always @(posedge clk) if (opfetch)
m1_lock128 <= !(din[7] ^ din[6]);
always @(posedge clk or posedge rst)
if (rst)
begin
fmaddr[4] <= 1'b0;
intmask <= 8'b1;
fddvirt <= 8'b0;
sysconf <= 8'h00; // 3.5 MHz
memconf <= 8'h04; // no map
cacheconf <= 4'h0; // no cache
rampage[0] <= 8'h00;
rampage[1] <= 8'h05;
rampage[2] <= 8'h02;
rampage[3] <= 8'h00;
`ifdef FDR
fdr_en <= 1'b0;
`endif
end
else // if (rst)
begin
if (p7ffd_wr)
begin
memconf[0] <= din[4];
rampage[3] <= {2'b0, lock128_3 ? {din[5], din[7:6]} : ({1'b0, lock128 ? 2'b0 : din[7:6]}), din[2:0]};
end
else if (portxt_wr)
begin
if (hoa[7:2] == RAMPAGE[7:2])
rampage[hoa[1:0]] <= din;
if (hoa == FMADDR)
fmaddr <= din[4:0];
if (hoa == SYSCONF)
begin
sysconf <= din;
cacheconf <= {4{din[2]}};
end
if (hoa == DMAWPD)
dmawpdev <= din[1:0];
if (hoa == CACHECONF)
cacheconf <= din[3:0];
if (hoa == MEMCONF)
memconf <= din;
if (hoa == FDDVIRT)
fddvirt <= din;
`ifdef FDR
if (hoa == FRCTRL)
fdr_en <= din[0];
`endif
if (hoa == INTMASK)
intmask <= din;
end
end
// 7FFD port
reg lock48;
assign p7ffd_wr = !a[15] && (loa==PORTFD) && iowr_s && !lock48;
always @(posedge clk or posedge rst)
if (rst)
lock48 <= 1'b0;
else if (p7ffd_wr && !lock128_3)
lock48 <= din[5];
// AY control
wire ay_hit = (loa==PORTFD) & a[15];
assign ay_bc1 = ay_hit & a[14] & iordwr;
assign ay_bdir = ay_hit & iowr;
// VG93
reg [1:0] drive_sel_raw;
wire [3:0] fddvrt = fddvirt[3:0];
wire virt_vg = fddvrt[drive_sel_raw];
assign open_vg = fddvirt[7];
assign drive_sel = {drive_sel_raw[1], drive_sel_raw[0] ^ cfg_floppy_swap};
wire vg_wen = (dos || open_vg) && !vdos && !virt_vg;
assign vg_cs_n = !(iordwr && vg_port && vg_wen);
assign vg_wrFF = iowr_s && vgsys_port && vg_wen;
wire vg_wrDS = iowr_s && vgsys_port && (dos || open_vg);
assign vdos_on = iordwr_s && (vg_port || vgsys_port) && dos && !vdos && virt_vg;
assign vdos_off = iordwr_s && vg_port && vdos;
// write drive number
always @(posedge clk)
if (vg_wrDS)
drive_sel_raw <= din;
// SD card (Z-controller compatible)
wire sdcfg_wr;
wire sddat_wr;
wire sddat_rd;
assign sdcfg_wr = (loa==SDCFG) && iowr_s;
assign sddat_wr = (loa==SDDAT) && iowr_s;
assign sddat_rd = (loa==SDDAT) && iord_s;
// SDCFG write - sdcs_n control
always @(posedge clk or posedge rst)
if (rst)
begin
spi_cs_n <= 4'b1111;
spi_mode <= 1'b0;
end
else if (sdcfg_wr)
begin
spi_cs_n <= {~din[4:2], din[1]};
// spi_mode <= din[7];
end
// start signal for SPI module with resyncing to fclk
assign sd_start = sddat_wr || sddat_rd;
// data for SPI module
assign sd_datain = wr ? din : 8'hFF;
// xxF7
wire portf7_wr = ((loa==PORTF7) && (a[8]==1'b1) && port_wr && (!dos || vdos));
wire portf7_rd = ((loa==PORTF7) && (a[8]==1'b1) && port_rd && (!dos || vdos));
// EFF7 port
reg [7:0] peff7;
always @(posedge clk or posedge rst)
if (rst)
peff7 <= 8'h00;
else if (!a[12] && portf7_wr && !dos) // #EEF7 in dos is not accessible
peff7 <= din;
// gluclock ports
assign gluclock_on = peff7[7] || dos; // in dos mode EEF7 is not accessible, gluclock access is ON in dos mode.
// comports
wire comport_wr = ((loa == COMPORT) && port_wr);
wire comport_rd = ((loa == COMPORT) && port_rd);
// write to wait registers
always @(posedge zclk)
begin
// gluclocks
if (gluclock_on && portf7_wr)
begin
if (!a[14]) // $BFF7 - data reg
wait_write <= din;
if (!a[13]) // $DFF7 - addr reg
wait_addr <= din;
end
// com ports
if (comport_wr) // $xxEF
wait_write <= din;
if (comport_wr || comport_rd)
wait_addr <= a[15:8];
if ((loa==PORTXT) && (hoa == DMAWPA))
wait_addr <= din;
end
// wait from wait registers
assign wait_start_gluclock = (gluclock_on && !a[14] && (portf7_rd || portf7_wr)); // $BFF7 - gluclock r/w
assign wait_start_comport = (comport_rd || comport_wr);
`ifdef IDE_HDD
// IDE ports
// do NOT generate IDE write, if neither of ide_wrhi|lo latches set and writing to NIDE10
wire ide_cs0 = ide_even && !ide_portc8;
wire ide_cs1 = ide_portc8;
wire ide_rd = rd && !(ide_rd_latch && ide_port10);
wire ide_wr = wr && !(!ide_wrlo_latch && !ide_wrhi_latch && ide_port10);
assign ide_req = iorq_s && ide_even && (ide_rd || ide_wr);
assign ide_cs0_n = !ide_cs0;
assign ide_cs1_n = !ide_cs1;
always @(posedge clk)
if (ide_req)
ide_stall <= 1'b1;
else if (ide_ready)
ide_stall <= 1'b0;
// control read & write triggers, which allow nemo-divide mod to work.
// read trigger:
reg ide_rd_trig; // nemo-divide read trigger
always @(posedge zclk)
begin
if (ide_port10 && port_rd && !ide_rd_trig)
ide_rd_trig <= 1'b1;
else if (ide_all && (port_rd || port_wr))
ide_rd_trig <= 1'b0;
end
// two triggers for write sequence
reg ide_wrlo_trig, ide_wrhi_trig; // nemo-divide write triggers
always @(posedge zclk)
if (ide_all && (port_rd || port_wr))
begin
if (ide_port11 && port_wr)
ide_wrhi_trig <= 1'b1;
else
ide_wrhi_trig <= 1'b0;
if (ide_port10 && port_wr && !ide_wrhi_trig && !ide_wrlo_trig)
ide_wrlo_trig <= 1'b1;
else
ide_wrlo_trig <= 1'b0;
end
// normal read: #10(low), #11(high)
// divide read: #10(low), #10(high)
//
// normal write: #11(high), #10(low)
// divide write: #10(low), #10(high)
reg [15:0] idewrreg; // write register, either low or high part is pre-written here,
// while other part is out directly from Z80 bus
always @(posedge zclk)
begin
if (port_wr && ide_port11)
idewrreg[15:8] <= din;
if (port_wr && ide_port10 && !ide_wrlo_trig)
idewrreg[ 7:0] <= din;
end
// generate read cycles for IDE as usual, except for reading #10
// instead of #11 for high byte (nemo-divide). I use additional latch
// since 'ide_rd_trig' clears during second Z80 IO read cycle to #10
reg ide_rd_latch; // to save state of trigger during read cycle
always @*
if (!rd)
ide_rd_latch <= ide_rd_trig;
reg ide_wrlo_latch, ide_wrhi_latch; // save state during write cycles
always @*
if (!wr)
begin
ide_wrlo_latch <= ide_wrlo_trig; // same for write triggers
ide_wrhi_latch <= ide_wrhi_trig;
end
// data read by Z80 from IDE
wire idein_lo_rd = port_rd && ide_port10 && (!ide_rd_trig); // while high part is remembered here
wire [7:0] iderdodd = iderdreg[15:8]; // read data from "odd" port (#11)
wire [7:0] iderdeven = (ide_rd_latch && ide_port10) ? iderdreg[15:8] : iderdreg[7:0]; // to control read data from "even" ide ports (all except #11)
// wire [7:0] iderdeven = (ide_rd_latch && ide_port10) ? idehiin[7:0] : ide_in[7:0]; // to control read data from "even" ide ports (all except #11)
reg [15:0] iderdreg;
// reg [7:0] idehiin; // IDE high part read register: low part is read directly to Z80 bus,
always @(posedge clk)
if (ide_stb)
iderdreg <= ide_in;
// if (idein_lo_rd)
// idehiin <= ide_in[15:8];
// data written to IDE from Z80
wire [7:0] ideout1 = ide_wrhi_latch ? idewrreg[15:8] : din[ 7:0];
wire [7:0] ideout0 = ide_wrlo_latch ? idewrreg[ 7:0] : din[ 7:0];
assign ide_out = {ideout1, ideout0};
`endif
endmodule

99
rtl/z80/zsignals.v Normal file
View File

@ -0,0 +1,99 @@
// Decoding and strobing of z80 signals
`include "tune.v"
module zsignals
(
// clocks
input wire clk,
input wire zpos,
// z80 interface input
input wire rst_n,
input wire iorq_n,
input wire mreq_n,
input wire m1_n,
input wire rfsh_n,
input wire rd_n,
input wire wr_n,
// Z80 signals
output wire rst,
output wire m1,
output wire rfsh,
output wire rd,
output wire wr,
output wire iorq,
output wire mreq,
output wire rdwr,
output wire iord,
output wire iowr,
output wire iordwr,
output wire memrd,
output wire memwr,
output wire memrw,
output wire opfetch,
output wire intack,
// Z80 signals strobes, at fclk
output wire iorq_s,
output wire mreq_s,
output wire iord_s,
output wire iowr_s,
output wire iordwr_s,
output wire memrd_s,
output wire memwr_s,
output wire memrw_s,
output wire opfetch_s
);
reg [1:0] iorq_r = 0, mreq_r = 0;
// invertors
assign rst = !rst_n;
assign m1 = !m1_n;
assign rfsh = !rfsh_n;
assign rd = !rd_n;
assign wr = !wr_n;
// requests
assign iorq = !iorq_n && m1_n; // this is masked by ~M1 to avoid port decoding on INT ack
assign mreq = !mreq_n && rfsh_n; // this is masked by ~RFSH to ignore refresh cycles as memory requests
// combined
assign rdwr = rd || wr;
assign iord = iorq && rd;
assign iowr = iorq && wr;
assign iordwr = iorq && rdwr;
assign memrd = mreq && rd;
assign memwr = mreq && !rd;
assign memrw = mreq && rdwr;
assign opfetch = memrd && m1;
assign intack = !iorq_n && m1; // NOT masked by M1
// strobed
assign iorq_s = iorq_r[0] && !iorq_r[1];
assign mreq_s = mreq_r[0] && !mreq_r[1];
assign iord_s = iorq_s && rd;
assign iowr_s = iorq_s && wr;
assign iordwr_s = iorq_s && rdwr;
assign memrd_s = mreq_s && rd;
assign memwr_s = mreq_s && !rd;
assign memrw_s = mreq_s && rdwr;
assign opfetch_s = memrd_s && m1;
// latch inputs on FPGA clock
always @(posedge clk) if (zpos)
begin
iorq_r[0] <= iorq;
mreq_r[0] <= mreq;
end
always @(posedge clk)
begin
iorq_r[1] <= iorq_r[0];
mreq_r[1] <= mreq_r[0];
end
endmodule