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 files.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 -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|*} -hold 1
set_multicycle_path -to {tsconf|CPU|*} -setup 2
set_multicycle_path -to {tsconf|CPU|*} -hold 1
set_multicycle_path -to {tsconf|U15|*} -setup 2
set_multicycle_path -to {tsconf|U15|*} -hold 1
set_multicycle_path -to {tsconf|saa1099|*} -setup 2
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 ""}
{ "" "" "" "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 ""}
{ "" "" "" "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 ""}
{ "" "" "" "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 ""}

View File

@ -301,9 +301,7 @@ sd_card sd_card
//////////////////// MAIN //////////////////////
wire [7:0] R,G,B;
wire HBlank,VBlank;
wire VS, HS;
wire ce_vid;
wire [15:0] SOUND_L;
wire [15:0] SOUND_R;
@ -324,14 +322,11 @@ tsconf tsconf
.SDRAM_nCS(SDRAM_nCS),
// .SDRAM_CLK(SDRAM_CLK),
.VGA_R(R),
.VGA_G(G),
.VGA_B(B),
.VGA_HS(HS),
.VGA_VS(VS),
.VGA_HBLANK(HBlank),
.VGA_VBLANK(VBlank),
.VGA_CEPIX(ce_vid),
.VRED(R),
.VGRN(G),
.VBLU(B),
.VHSYNC(HS),
.VVSYNC(VS),
.SD_SO(sdmiso),
.SD_SI(sdmosi),
@ -345,12 +340,13 @@ tsconf tsconf
.WARM_RESET(buttons[1]),
.RTC(rtc),
.OUT0(~status[30]),
.TAPE_IN(UART_RX),
.CMOSCfg(CMOSCfg),
.PS2_KEY({key_strobe,key_pressed,key_extended,key_code}),
.PS2_MOUSE(ps2_mouse),
.joystick(joystick_0[5:0] | joystick_1[5:0]),
.JOYSTICK(joystick_0 | joystick_1),
.loader_act(ioctl_download),
.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 VERILOG_FILE rtl/memory/dma.v
set_global_assignment -name VERILOG_FILE rtl/memory/arbiter.v
set_global_assignment -name VERILOG_FILE rtl/memory/sdram.v
set_global_assignment -name VERILOG_FILE rtl/memory/dpram.v
set_global_assignment -name VERILOG_FILE rtl/common/zsignals.v
set_global_assignment -name VERILOG_FILE rtl/common/zports.v
set_global_assignment -name VERILOG_FILE rtl/common/zmem.v
set_global_assignment -name VERILOG_FILE rtl/common/zmaps.v
set_global_assignment -name VERILOG_FILE rtl/common/zint.v
set_global_assignment -name VERILOG_FILE rtl/common/zclock.v
set_global_assignment -name VERILOG_FILE rtl/rtc/mc146818a.v
set_global_assignment -name VERILOG_FILE rtl/common/clock.v
set_global_assignment -name VERILOG_FILE rtl/common/dma.v
set_global_assignment -name VERILOG_FILE rtl/common/resetter.v
set_global_assignment -name VERILOG_FILE rtl/common/spi.v
set_global_assignment -name VERILOG_FILE rtl/dram/arbiter.v
set_global_assignment -name VERILOG_FILE rtl/dram/dpram.v
set_global_assignment -name VERILOG_FILE rtl/dram/sdram.v
set_global_assignment -name VERILOG_FILE rtl/periph/kempston_mouse.v
set_global_assignment -name VHDL_FILE rtl/periph/keyboard.vhd
set_global_assignment -name VERILOG_FILE rtl/periph/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 QIP_FILE rtl/sound/jt12/jt03.qip
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 VERILOG_FILE rtl/sound/gs.v
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.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_fetch.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/kempston_mouse.v
set_global_assignment -name VERILOG_FILE rtl/spi.v
set_global_assignment -name VERILOG_FILE rtl/clock.v
set_global_assignment -name VERILOG_FILE rtl/z80/zclock.v
set_global_assignment -name VERILOG_FILE rtl/z80/zint.v
set_global_assignment -name VERILOG_FILE rtl/z80/zmaps.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 SDC_FILE TSConf.sdc
set_global_assignment -name SYSTEMVERILOG_FILE TSConf.sv
set_global_assignment -name SEARCH_PATH rtl/

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,19 +1,22 @@
// This module serves direct DRAM-to-device data transfer
// to do
// - probably add the extra 8 bit counter for number of bursts
`include "tune.v"
module dma
(
// clocks
input wire clk,
input wire c2,
input wire reset,
input wire rst_n,
// interface
`ifdef FDR
input wire [9:0] dmaport_wr,
`else
input wire [8:0] dmaport_wr,
`endif
output wire dma_act,
output reg [15:0] data,
output reg [15:0] data = 0,
output wire [ 7:0] wraddr,
output wire int_start,
@ -34,6 +37,12 @@ module dma
output wire spi_req,
input wire spi_stb,
// WTPORT interface
input wire [7:0] wtp_rddata,
// output wire [7:0] wtp_wrdata,
output wire wtp_req,
input wire wtp_stb,
// IDE interface
input wire [15:0] ide_in,
output wire [15:0] ide_out,
@ -41,6 +50,14 @@ module dma
output wire ide_rnw,
input wire ide_stb,
`ifdef FDR
// 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,
@ -52,8 +69,6 @@ module dma
// 0 - device to RAM (read from device)
// 1 - RAM to device (write to device)
assign wraddr = d_addr[7:0];
wire dma_saddrl = dmaport_wr[0];
wire dma_saddrh = dmaport_wr[1];
wire dma_saddrx = dmaport_wr[2];
@ -63,72 +78,84 @@ module dma
wire dma_len = dmaport_wr[6];
wire dma_launch = dmaport_wr[7];
wire dma_num = dmaport_wr[8];
`ifdef FDR
wire dma_numh = dmaport_wr[9];
`endif
// 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;
// devices
localparam DEV_RAM = 3'b0001;
// devices
localparam DEV_RAM = 3'b001;
localparam DEV_BLT1 = 4'b1001;
`ifdef XTR_FEAT
localparam DEV_BLT2 = 4'b0110;
`endif
localparam DEV_FIL = 4'b0100;
localparam DEV_SPI = 3'b010;
localparam DEV_IDE = 3'b011;
localparam DEV_CRM = 4'b1100;
localparam DEV_SFL = 4'b1101;
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 [3:0] dev_uni = device[3:0]; // unidirectional
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_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_spi = (dev_bid == DEV_SPI);
wire dv_ide = (dev_bid == DEV_IDE);
wire dv_crm = (dev_uni == DEV_CRM);
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_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
wire 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;
// 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;
`ifdef FDR
assign byte_sw_stb = spi_int_stb || wtp_int_stb || fdr_int_stb;
`else
assign byte_sw_stb = spi_int_stb || wtp_int_stb;
`endif
// blitter
wire [15:0] blt_rddata = (dev_uni == DEV_BLT1) ? blt1_rddata : blt2_rddata;
// 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 [3:0] blt1_data3 = |data[15:12] ? data[15:12] : dram_rddata[15:12];
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 [7:0] blt1_data32 = |data[15:8] ? data[15:8] : dram_rddata[15:8];
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
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;
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_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 [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
always @(posedge clk)
if (state_rd)
begin
if (dram_next)
data <= (dv_blt && phase_blt) ? blt_rddata : dram_rddata;
`else // XTR_FEAT
wire [15:0] blt_rddata = blt1_rddata;
`endif
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
end
// states
// states
wire state_rd = ~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);
// states processing
wire phase_end = phase_end_ram || phase_end_dev;
// states processing
wire blt_hook = dv_blt && !phase_blt && !phase;
wire phase_end_ram = state_mem && dram_next && !blt_hook;
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 phase_blt_end = state_mem && dram_next && !phase;
@ -187,15 +202,6 @@ module dma
// 0 1 0 read 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)
if (dma_launch) // write to DMACtrl - launch of DMA burst
begin
@ -215,26 +221,75 @@ module dma
phase <= ~phase;
if (phase_blt_end)
phase_blt <= ~phase_blt;
if (spi_int_stb)
if (byte_sw_stb)
bsel <= ~bsel;
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_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 [8:0] n_ctr; // counter for bursts
wire [8:0] n_ctr_dec = n_ctr - next_burst;
assign dma_act = !n_ctr[8];
reg [7:0] b_ctr; // counter for cycles in burst
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];
`endif
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;
`endif
else if (dma_launch)
begin
@ -248,31 +303,36 @@ module dma
n_ctr <= n_ctr_dec;
end
// loading of burst parameters
// loading of burst parameters
always @(posedge clk)
begin
if (dma_len)
b_len <= zdata;
if (dma_num)
`ifdef FDR
b_num[7:0] <= zdata;
if (dma_numh)
b_num[9:8] <= zdata[1:0];
`else
b_num <= zdata;
`endif
end
// address processing
// address processing
// 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 [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)
if ((dram_next || dev_stb) && state_rd && (!dv_blt || !phase_blt)) // increment RAM source addr
s_addr <= s_addr_next;
@ -296,16 +356,16 @@ module dma
end
// 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 [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)
if ((dram_next || dev_stb) && state_wr) // increment RAM dest addr
d_addr <= d_addr_next;
@ -328,10 +388,42 @@ module dma
end
// INT generation
reg dma_act_r;
reg dma_act_r = 0;
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 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

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,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

@ -59,17 +59,17 @@ always @(*) begin
8'h0c : Dout <= c_reg;
8'h0d : Dout <= 8'b10000000;
8'hb1 : Dout <= CMOSCfg[7:6]; // CPU Speed
8'hb2 : Dout <= 0; // Boot device
8'hb3 : Dout <= CMOSCfg[8]; // CPU Cache
8'hb4 : Dout <= CMOSCfg[13:11]; // F11
8'hb5 : Dout <= CMOSCfg[15:14]; // F11 bank
8'hb6 : Dout <= CMOSCfg[18:16]; // Shift+F11
8'hb7 : Dout <= CMOSCfg[20:19]; // Shift+F11 bank
8'hb8 : Dout <= CMOSCfg[10:9]; // #7FFD
8'hb9 : Dout <= CMOSCfg[23:21]; // ZX Palette
8'hba : Dout <= CMOSCfg[24]; // NGS Reset
8'hbb : Dout <= CMOSCfg[27:25]; // INT offset
// 8'hb1 : Dout <= CMOSCfg[7:6]; // CPU Speed
// 8'hb2 : Dout <= 0; // Boot device
// 8'hb3 : Dout <= CMOSCfg[8]; // CPU Cache
// 8'hb4 : Dout <= CMOSCfg[13:11]; // F11
// 8'hb5 : Dout <= CMOSCfg[15:14]; // F11 bank
// 8'hb6 : Dout <= CMOSCfg[18:16]; // Shift+F11
// 8'hb7 : Dout <= CMOSCfg[20:19]; // Shift+F11 bank
// 8'hb8 : Dout <= CMOSCfg[10:9]; // #7FFD
// 8'hb9 : Dout <= CMOSCfg[23:21]; // ZX Palette
// 8'hba : Dout <= CMOSCfg[24]; // NGS Reset
// 8'hbb : Dout <= CMOSCfg[27:25]; // INT offset
8'hf0 : Dout <= KEYSCANCODE;
default: Dout <= CMOS_Dout;
@ -233,7 +233,7 @@ always @(posedge CLK) begin
end
// 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),
.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,5 +1,7 @@
// This module fetches video data from DRAM
`include "tune.v"
module video_fetch
(
// clocks
@ -19,15 +21,15 @@ module video_fetch
input wire [15:0] video_data
);
// fetching data
always @(posedge clk) if (video_strobe)
begin
always @(posedge clk) if (video_strobe)
begin
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[2]) fetch_temp[23:16] <= video_data[ 7:0];
if (f_sel[3]) fetch_temp[31:24] <= video_data[15:8];
end
end
always @(posedge clk) if (fetch_stb) fetch_data <= fetch_temp;
always @(posedge clk) if (fetch_stb)
fetch_data <= fetch_temp;
endmodule

View File

@ -1,6 +1,7 @@
// This module decodes video modes
`include "tune.v"
module video_mode
(
// clocks
@ -10,6 +11,7 @@ module video_mode
input wire [7:0] vpage,
input wire [7:0] vconf,
input wire ts_rres_ext,
input wire v60hz,
// video parameters & mode controls
input wire [8:0] gx_offs,
@ -30,6 +32,7 @@ module video_mode
input wire pix_start,
input wire line_start_s,
output wire tv_hires,
output reg vga_hires = 0,
output wire [1:0] render_mode,
output wire pix_stb,
output wire fetch_stb,
@ -47,189 +50,181 @@ module video_mode
output wire [ 4:0] video_bw
);
wire [1:0] vmod = vconf[1:0];
wire [1:0] rres = vconf[7:6];
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;
// 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
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;
// 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 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];
// 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];
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
// fetch selectors
// Attention: counter is already incremented at the time of video data fetching!
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
// 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};
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
// 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];
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
assign fetch_bsl = (vmod == M_TX) ? f_txt_bsl[cnt_col[1:0]] : 2'b10;
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
// 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];
// [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];
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
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
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
// 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];
// X offset
assign x_offs_mode = {vmod == M_XC ? {gx_offs[8:1], 1'b0} : {1'b0, gx_offs[8:1]}, gx_offs[0]};
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
// DRAM bandwidth usage
localparam BW2 = 2'b00;
localparam BW4 = 2'b01;
localparam BW8 = 2'b11;
`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)
localparam BU1 = 3'b001;
localparam BU2 = 3'b010;
localparam BU4 = 3'b100;
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)
// [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];
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
// pixelrate
wire [3:0] pixrate = 4'b1000; // change these if you change the modes indexes!
assign tv_hires = pixrate[vmod];
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];
// 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];
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};
// 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];
// 16c
wire [20:0] addr_16c = {vpage[7:3], cnt_row, cnt_col[6:0]};
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)
// 256c
wire [20:0] addr_256c = {vpage[7:4], cnt_row, cnt_col[7: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];
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];
// 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];
// ZX
wire [20:0] addr_zx = {vpage, 1'b0, ~cnt_col[0] ? addr_zx_gfx : addr_zx_atr};
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]};
// 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 [20:0] addr_text = {vpage[7:1], addr_tx[cnt_col[1:0]]};
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]
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]
// 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,17 +2,23 @@
// This module generates video for DAC
// (c)2015 TSL
`include "tune.v"
module video_out
(
// clocks
input wire clk, c3,
// video controls
input wire vga_on,
input wire tv_blank,
input wire vga_blank,
input wire vga_line,
input wire [1:0] plex_sel_in,
// mode controls
input wire tv_hires,
input wire vga_hires,
input wire [3:0] palsel,
// Z80 pins
@ -22,39 +28,155 @@ module video_out
// video data
input wire [7:0] vplex_in,
output wire [7:0] vred,
output wire [7:0] vgrn,
output wire [7:0] vblu,
input wire [7:0] vgaplex,
output wire [1:0] vred,
output wire [1:0] vgrn,
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;
always @(posedge clk) if (c3) vplex <= vplex_in;
reg blank1; // GOVNOKOD!!!!!!!!!!!!!!!!!!!!!
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
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),
.address_a(cram_addr_in),
.data_a (cram_data_in),
.wren_a (cram_we),
.address_b(vdata),
.q_b (vpixel)
);
reg blank;
always @(posedge clk) blank <= tv_blank;
wire [14:0] vpix = blank ? 15'b0 : vpixel[14:0];
assign vred = {vpix[14:10], vpix[14:12]};
assign vgrn = {vpix[ 9: 5], vpix[ 9: 7]};
assign vblu = {vpix[ 4: 0], vpix[ 4: 2]};
assign vdac_mode = vpixel[15];
);
/*
altdpram video_cram
(
.inclock (clk),
.data (cram_data_in),
.rdaddress (vdata),
.wraddress (cram_addr_in),
.wren (cram_we),
.q (vpixel),
.aclr (1'b0),
.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

View File

@ -1,5 +1,6 @@
// This module latches all port parameters for video from Z80
`include "tune.v"
module video_ports
(
@ -40,53 +41,59 @@ module video_ports
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
output reg [7:0] border = 0,
output reg [7:0] vpage = 0,
output reg [7:0] vconf = 0,
output reg [8:0] gx_offs = 0,
output reg [8:0] gy_offs = 0,
output reg [8:0] t0x_offs = 0,
output reg [8:0] t0y_offs = 0,
output reg [8:0] t1x_offs = 0,
output reg [8:0] t1y_offs = 0,
output reg [7:0] palsel = 0,
output reg [7:0] hint_beg = 0,
output reg [8:0] vint_beg = 0,
output reg [7:0] tsconf = 0,
output reg [7:0] tmpage = 0,
output reg [7:0] t0gpage = 0,
output reg [7:0] t1gpage = 0,
output reg [7:0] sgpage = 0
);
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;
reg [7:0] vpage_r = 0;
reg [7:0] vconf_r = 0;
reg [7:0] t0gpage_r = 0;
reg [7:0] t1gpage_r = 0;
reg [8:0] gx_offs_r = 0;
reg [8:0] t0x_offs_r = 0;
reg [8:0] t1x_offs_r = 0;
reg [7:0] palsel_r = 0;
reg [3:0] vint_inc = 0;
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
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;
always @(posedge clk) begin
if (res) begin
always @(posedge clk or posedge res)
if (res)
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
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;
end
always @(posedge clk) begin
if (res) begin
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;
@ -95,7 +102,9 @@ always @(posedge clk) begin
tsconf <= 8'b0;
hint_beg <= 8'd1;
end
else begin
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;
@ -122,21 +131,30 @@ always @(posedge clk) begin
if (t0gpage_wr ) t0gpage_r <= d;
if (t1gpage_wr ) t1gpage_r <= d;
end
end
// latching regs at line start, delaying hires for 1 line
always @(posedge clk) begin
// 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
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;
@ -144,6 +162,5 @@ always @(posedge clk) begin
t0gpage <= t0gpage_r;
t1gpage <= t1gpage_r;
end
end
endmodule
endmodule

View File

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

View File

@ -1,11 +1,11 @@
`include "tune.v"
// This module generates all video raster signals
// This module generates video raster signals
module video_sync
(
// 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,
@ -26,8 +26,12 @@ module video_sync
// video syncs
output reg hsync,
output reg vsync,
output reg csync,
// video controls
input wire cfg_60hz,
input wire vga_on,
output reg v60hz = 0,
input wire nogfx,
output wire v_pf,
output wire hpix,
@ -35,8 +39,9 @@ module video_sync
output wire v_ts,
output wire hvpix,
output wire hvtspix,
output wire tv_hblank,
output wire tv_vblank,
output wire tv_blank,
output wire vga_blank,
output wire vga_line,
output wire frame_start,
output wire line_start_s,
output wire pix_start,
@ -45,125 +50,194 @@ module video_sync
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,
output reg [7:0] cnt_col,
output reg [8:0] cnt_row,
output reg cptr,
output reg [3:0] scnt,
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,
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 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;
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
// counters
reg [8:0] hcount = 0;
reg [8:0] vcount = 0;
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;
// horizontal TV (7 MHz)
always @(posedge clk) if (c3) hcount <= line_start ? 9'b0 : hcount + 9'b1;
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;
// vertical TV (15.625 kHz)
always @(posedge clk) if (line_start_s) vcount <= (vcount == (VPERIOD - 1)) ? 9'b0 : vcount + 9'b1;
`ifdef PENT_312
assign hcnt = hcount[4:0];
assign upper8 = vcount < 8;
`endif
// column address for DRAM
always @(posedge clk) begin
if (line_start2) begin
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
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);
// row address for DRAM
reg y_offs_wr_r;
assign tv_hblank = (hcount > HBLNK_BEG) && (hcount <= HBLNK_END);
assign tv_vblank = (vcount >= VBLNK_BEG) && (vcount < VBLNK_END);
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;
assign hvpix = hpix && vpix;
// pixel counter
always @(posedge clk) if (pix_stb) // f1 or c3
scnt <= pix_start ? 4'b0 : scnt + 4'b1;
assign hpix = (hcount >= hpix_beg) && (hcount < hpix_end);
// TS-line counter
assign ts_raddr = hcount - hpix_beg_ts;
assign vpix = (vcount >= vpix_beg) && (vcount < vpix_end);
always @(posedge clk)
if (ts_start_coarse)
lcount <= vcount - vpix_beg_ts + 9'b1;
assign hvtspix = htspix && vtspix;
wire htspix = (hcount >= hpix_beg_ts) && (hcount < hpix_end_ts);
wire vtspix = (vcount >= vpix_beg_ts) && (vcount < vpix_end_ts);
// 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;
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
// FLASH generator
reg [4:0] flash_ctr;
assign frame = flash_ctr[0];
assign flash = flash_ctr[4];
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 (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
wire line_start = hcount == (HPERIOD - 1);
assign line_start_s = line_start && c3;
wire line_start2 = hcount == (HSYNC_END - 1);
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;
// sync strobes
wire vga_hblank1 = (cnt_out > 9'd359);
always @(posedge clk) if (f1) // fix me - bydlocode !!!
vga_hblank <= vga_hblank1;
always @(posedge clk) begin
hsync <= hs;
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;
end
csync <= ~(vs ^ hs);
end
endmodule

View File

@ -1,28 +1,34 @@
`include "tune.v"
// This module is a video top-level
module video_top
(
// clocks
input wire clk,
input wire f0, f1,
input wire h0, h1,
input wire c0, c1, c2, c3,
input wire h1,
input wire c0, c1, c3,
// input wire t0, // debug!!!
// video DAC
output wire [7:0] vred,
output wire [7:0] vgrn,
output wire [7:0] vblu,
output wire [1:0] vred,
output wire [1:0] vgrn,
output wire [1:0] vblu,
// video raw (for 15 bit DAC)
output wire [4:0] vred_raw,
output wire [4:0] vgrn_raw,
output wire [4:0] vblu_raw,
output wire vdac_mode,
`ifdef IDE_VDAC2
output wire vdac2_msel,
`endif
// video syncs
output wire hsync,
output wire vsync,
output wire hblank,
output wire vblank,
output wire pix_stb,
output wire csync,
// Z80 controls
input wire [ 7:0] d,
@ -63,15 +69,17 @@ module video_top
input wire res,
output wire int_start,
output wire line_start_s,
`ifdef PENT_312
output wire [4:0] hcnt,
output wire upper8,
`endif
// DRAM interface
output wire [20:0] video_addr,
output wire [ 4:0] video_bw,
output wire video_go,
input wire [15:0] dram_rdata, // raw, should be latched by c2 (video_next)
input wire video_next,
input wire [15:0] dram_rdata, // raw, should be latched by c2
input wire video_pre_next,
input wire next_video,
input wire video_strobe,
output wire [20:0] ts_addr,
output wire ts_req,
@ -79,96 +87,123 @@ module video_top
input wire ts_next,
output wire [20:0] tm_addr,
output wire tm_req,
input wire tm_next
input wire tm_next,
// video controls
input wire cfg_60hz,
input wire vga_on
);
// video config
wire [7:0] vpage; // re-latched at line_start
wire [7:0] vconf; //
wire [8:0] gx_offs; //
wire [8:0] gy_offs; //
wire [7:0] palsel; //
wire [8:0] t0x_offs; //
wire [8:0] t1x_offs; //
wire [7:0] t0gpage; //
wire [7:0] t1gpage; //
wire [7:0] sgpage; // * not yet !!!
wire [8:0] t0y_offs;
wire [8:0] t1y_offs;
wire [7:0] tsconf;
wire [7:0] tmpage;
wire [7:0] hint_beg;
wire [8:0] vint_beg;
wire [8:0] hpix_beg;
wire [8:0] hpix_end;
wire [8:0] vpix_beg;
wire [8:0] vpix_end;
wire [8:0] hpix_beg_ts;
wire [8:0] hpix_end_ts;
wire [8:0] vpix_beg_ts;
wire [8:0] vpix_end_ts;
wire [5:0] x_tiles;
wire [9:0] x_offs_mode;
wire [4:0] go_offs;
wire [1:0] render_mode;
wire tv_hires;
wire vga_hires;
wire v60hz;
wire nogfx = vconf[5];
wire notsu = vconf[4];
`ifdef IDE_VDAC2
assign vdac2_msel = vconf[2];
`endif
wire tv_blank;
// video config
wire [7:0] vpage; // re-latched at line_start
wire [7:0] vconf; //
wire [8:0] gx_offs; //
wire [8:0] gy_offs; //
wire [7:0] palsel; //
wire [8:0] t0x_offs; //
wire [8:0] t1x_offs; //
wire [7:0] t0gpage; //
wire [7:0] t1gpage; //
wire [7:0] sgpage; // * not yet !!!
wire [8:0] t0y_offs;
wire [8:0] t1y_offs;
wire [7:0] tsconf;
wire [7:0] tmpage;
wire [7:0] hint_beg;
wire [8:0] vint_beg;
wire [8:0] hpix_beg;
wire [8:0] hpix_end;
wire [8:0] vpix_beg;
wire [8:0] vpix_end;
wire [8:0] hpix_beg_ts;
wire [8:0] hpix_end_ts;
wire [8:0] vpix_beg_ts;
wire [8:0] vpix_end_ts;
wire [5:0] x_tiles;
wire [9:0] x_offs_mode;
wire [4:0] go_offs;
wire [1:0] render_mode;
wire tv_hires;
wire nogfx = vconf[5];
wire notsu = vconf[4];
wire gfxovr = vconf[3];
wire tv_hblank;
wire tv_vblank;
// counters
wire [7:0] cnt_col;
wire [8:0] cnt_row;
wire cptr;
wire [3:0] scnt;
wire [8:0] lcount;
// counters
wire [7:0] cnt_col;
wire [8:0] cnt_row;
wire cptr;
wire [3:0] scnt;
wire [8:0] lcount;
// synchro
wire frame_start;
wire pix_start;
wire tv_pix_start;
wire vga_pix_start;
wire ts_start;
wire vga_blank;
wire vga_line;
wire v_ts;
wire v_pf;
wire hpix;
wire vpix;
wire hvpix;
wire hvtspix;
wire frame;
wire flash;
wire pix_stb;
// synchro
wire frame_start;
wire pix_start;
wire tv_pix_start;
wire ts_start;
wire v_ts;
wire v_pf;
wire hpix;
wire vpix;
wire hvpix;
wire hvtspix;
wire flash;
// fetcher
wire [31:0] fetch_data;
wire [31:0] fetch_temp;
wire [3:0] fetch_sel;
wire [1:0] fetch_bsl;
wire fetch_stb;
// fetcher
wire [31:0] fetch_data;
wire [31:0] fetch_temp;
wire [3:0] fetch_sel;
wire [1:0] fetch_bsl;
wire fetch_stb;
// video data
wire [7:0] border;
wire [7:0] vplex;
wire [7:0] vgaplex;
// video data
wire [7:0] border;
wire [7:0] vplex;
// TS
wire tsr_go;
wire [5:0] tsr_addr;
wire [8:0] tsr_line;
wire [7:0] tsr_page;
wire [8:0] tsr_x;
wire [2:0] tsr_xs;
wire tsr_xf;
wire [3:0] tsr_pal;
wire tsr_rdy;
// TS
wire tsr_go;
wire [5:0] tsr_addr;
wire [8:0] tsr_line;
wire [7:0] tsr_page;
wire [8:0] tsr_x;
wire [2:0] tsr_xs;
wire tsr_xf;
wire [3:0] tsr_pal;
wire tsr_rdy;
// TS-line
wire [8:0] ts_waddr;
wire [7:0] ts_wdata;
wire ts_we;
wire [8:0] ts_raddr;
// TS-line
wire [8:0] ts_waddr;
wire [7:0] ts_wdata;
wire ts_we;
wire [8:0] ts_raddr;
// VGA-line
wire [9:0] vga_cnt_in;
wire [9:0] vga_cnt_out;
video_ports video_ports
(
wire [7:0] ts_rdata0, ts_rdata1;
wire tl_act0 = lcount[0];
wire tl_act1 = ~lcount[0];
wire [8:0] ts_waddr0 = tl_act0 ? ts_raddr : ts_waddr;
wire [7:0] ts_wdata0 = tl_act0 ? 8'd0 : ts_wdata;
wire ts_we0 = tl_act0 ? c3 : ts_we;
wire [8:0] ts_waddr1 = tl_act1 ? ts_raddr : ts_waddr;
wire [7:0] ts_wdata1 = tl_act1 ? 8'd0 : ts_wdata;
wire ts_we1 = tl_act1 ? c3 : ts_we;
wire [7:0] ts_rdata = tl_act0 ? ts_rdata0 : ts_rdata1;
video_ports video_ports
(
.clk (clk),
.d (d),
.res (res),
@ -211,22 +246,26 @@ video_ports video_ports
.palsel (palsel),
.hint_beg (hint_beg),
.vint_beg (vint_beg),
.int_start (0),
`ifdef AUTO_INT
.int_start (int_start),
`else
.int_start (1'b0),
`endif
.tsconf (tsconf),
.tmpage (tmpage),
.t0gpage (t0gpage),
.t1gpage (t1gpage),
.sgpage (sgpage)
);
);
video_mode video_mode
(
video_mode video_mode
(
.clk (clk),
.f1 (f1),
.c3 (c3),
.vpage (vpage),
.vconf (vconf),
.v60hz (v60hz),
.fetch_sel (fetch_sel),
.fetch_bsl (fetch_bsl),
.fetch_cnt (scnt),
@ -234,7 +273,11 @@ video_mode video_mode
.txt_char (fetch_temp[15:0]),
.gx_offs (gx_offs),
.x_offs_mode (x_offs_mode),
`ifdef XTR_FEAT
.ts_rres_ext (tsconf[0]),
`else
.ts_rres_ext (1'b0),
`endif
.hpix_beg (hpix_beg),
.hpix_end (hpix_end),
.vpix_beg (vpix_beg),
@ -251,19 +294,18 @@ video_mode video_mode
.line_start_s (line_start_s),
.pix_start (pix_start),
.tv_hires (tv_hires),
.vga_hires (vga_hires),
.pix_stb (pix_stb),
.render_mode (render_mode),
.video_addr (video_addr),
.video_bw (video_bw)
);
);
video_sync video_sync
(
video_sync video_sync
(
.clk (clk),
.f1 (f1),
.c0 (c0),
.c1 (c1),
.c3 (c3),
.hpix_beg (hpix_beg),
.hpix_end (hpix_end),
@ -281,20 +323,29 @@ video_sync video_sync
.vint_beg (vint_beg),
.hsync (hsync),
.vsync (vsync),
.tv_hblank (tv_hblank),
.tv_vblank (tv_vblank),
.csync (csync),
.tv_blank (tv_blank),
.vga_blank (vga_blank),
.vga_cnt_in (vga_cnt_in),
.vga_cnt_out (vga_cnt_out),
.ts_raddr (ts_raddr),
.lcount (lcount),
.cnt_col (cnt_col),
.cnt_row (cnt_row),
.cptr (cptr),
.scnt (scnt),
`ifdef PENT_312
.hcnt (hcnt),
.upper8 (upper8),
`endif
.frame (frame),
.flash (flash),
.pix_stb (pix_stb),
.pix_start (pix_start),
.ts_start (ts_start),
.cstart (x_offs_mode[9:2]),
.rstart (gy_offs),
.vga_line (vga_line),
.frame_start (frame_start),
.int_start (int_start),
.v_pf (v_pf),
@ -304,13 +355,15 @@ video_sync video_sync
.hvpix (hvpix),
.hvtspix (hvtspix),
.nogfx (nogfx),
.vga_on (vga_on),
.cfg_60hz (cfg_60hz),
.v60hz (v60hz),
.video_go (video_go),
.video_pre_next(video_pre_next)
);
.video_pre_next (video_pre_next)
);
video_fetch video_fetch
(
video_fetch video_fetch
(
.clk (clk),
.f_sel (fetch_sel),
.b_sel (fetch_bsl),
@ -319,16 +372,23 @@ video_fetch video_fetch
.fetch_temp (fetch_temp),
.video_strobe (video_strobe),
.video_data (dram_rdata)
);
);
video_ts video_ts
(
video_ts video_ts
(
.clk (clk),
.start (ts_start),
.line (lcount),
.v_ts (v_ts),
`ifdef DISABLE_TSU
.tsconf (0),
`else
.tsconf (tsconf),
// .tsconf ({3'b0, tsconf[4:0]}), // no TSU
// .tsconf ({1'b0, tsconf[6:0]}), // no sprites
// .tsconf ({tsconf[7], 2'b00, tsconf[4:0]}), // no tiles
`endif
.t0gpage (t0gpage),
.t1gpage (t1gpage),
.sgpage (sgpage),
@ -360,10 +420,10 @@ video_ts video_ts
.sfile_addr_in (zma),
.sfile_data_in (zmd),
.sfile_we (sfile_we)
);
);
video_ts_render video_ts_render
(
video_ts_render video_ts_render
(
.clk (clk),
.reset (ts_start),
@ -387,18 +447,21 @@ video_ts_render video_ts_render
.dram_pre_next (ts_pre_next),
.dram_next (ts_next),
.dram_rdata (dram_rdata)
);
);
video_render video_render
(
video_render video_render
(
.clk (clk),
.c1 (c1),
.hvpix (hvpix),
.hvtspix (hvtspix),
.nogfx (nogfx),
.notsu (notsu),
.gfxovr (gfxovr),
`ifdef XTR_FEAT
.gfxovr (vconf[3]),
`else
.gfxovr (1'b0),
`endif
.flash (flash),
.hires (tv_hires),
.psel (scnt),
@ -408,60 +471,172 @@ video_render video_render
.border_in (border),
.tsdata_in (ts_rdata),
.vplex_out (vplex)
);
);
video_out video_out
(
video_out video_out
(
.clk (clk),
.c3 (c3),
.tv_blank (tv_hblank|tv_vblank),
.vga_on (vga_on),
.tv_blank (tv_blank),
.vga_blank (vga_blank),
.vga_line (vga_line),
.palsel (palsel[3:0]),
.plex_sel_in ({h1, f1}),
.tv_hires (tv_hires),
.vga_hires (vga_hires),
.cram_addr_in (zma),
.cram_data_in (zmd[15:0]),
.cram_we (cram_we),
.vplex_in (vplex),
.vgaplex (vgaplex),
.vred (vred),
.vgrn (vgrn),
.vblu (vblu),
.vred_raw (vred_raw),
.vgrn_raw (vgrn_raw),
.vblu_raw (vblu_raw),
.vdac_mode (vdac_mode)
);
);
assign hblank = tv_hblank;
assign vblank = tv_vblank;
// 2 buffers: 512 pixels * 8 bits (9x8) - used as bitmap buffer for TS overlay over graphics
// (2 altdprams)
wire tl_act0 = lcount[0];
wire tl_act1 = ~lcount[0];
wire [8:0] ts_waddr0 = tl_act0 ? ts_raddr : ts_waddr;
wire [7:0] ts_wdata0 = tl_act0 ? 8'd0 : ts_wdata;
wire ts_we0 = tl_act0 ? c3 : ts_we;
wire [8:0] ts_waddr1 = tl_act1 ? ts_raddr : ts_waddr;
wire [7:0] ts_wdata1 = tl_act1 ? 8'd0 : ts_wdata;
wire ts_we1 = tl_act1 ? c3 : ts_we;
wire [7:0] ts_rdata = tl_act0 ? ts_rdata0 : ts_rdata1;
wire [7:0] ts_rdata0, ts_rdata1;
dpram #(.ADDRWIDTH(9)) video_tsline0
(
// 2 buffers: 512 pixels * 8 bits (9x8) - used as bitmap buffer for TS overlay over graphics
dpram #(.ADDRWIDTH(9)) video_tsline0
(
.clock (clk),
.address_a (ts_waddr0),
.data_a (ts_wdata0),
.wren_a (ts_we0),
.address_b (ts_raddr),
.q_b (ts_rdata0)
);
dpram #(.ADDRWIDTH(9)) video_tsline1
(
);
dpram #(.ADDRWIDTH(9)) video_tsline1
(
.clock (clk),
.address_a (ts_waddr1),
.data_a (ts_wdata1),
.wren_a (ts_we1),
.address_b (ts_raddr),
.q_b (ts_rdata1)
);
);
/*
altdpram video_tsline0
(
.inclock (clk),
.wren (ts_we0),
.data (ts_wdata0),
.rdaddress (ts_raddr),
.wraddress (ts_waddr0),
.q (ts_rdata0),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclock (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_tsline0.indata_aclr = "OFF",
video_tsline0.indata_reg = "INCLOCK",
video_tsline0.intended_device_family = "ACEX1K",
video_tsline0.lpm_type = "altdpram",
video_tsline0.outdata_aclr = "OFF",
video_tsline0.outdata_reg = "UNREGISTERED",
video_tsline0.rdaddress_aclr = "OFF",
video_tsline0.rdaddress_reg = "INCLOCK",
video_tsline0.rdcontrol_aclr = "OFF",
video_tsline0.rdcontrol_reg = "UNREGISTERED",
video_tsline0.width = 8,
video_tsline0.widthad = 9,
video_tsline0.wraddress_aclr = "OFF",
video_tsline0.wraddress_reg = "INCLOCK",
video_tsline0.wrcontrol_aclr = "OFF",
video_tsline0.wrcontrol_reg = "INCLOCK";
altdpram video_tsline1
(
.inclock (clk),
.wren (ts_we1),
.data (ts_wdata1),
.rdaddress (ts_raddr),
.wraddress (ts_waddr1),
.q (ts_rdata1),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclock (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_tsline1.indata_aclr = "OFF",
video_tsline1.indata_reg = "INCLOCK",
video_tsline1.intended_device_family = "ACEX1K",
video_tsline1.lpm_type = "altdpram",
video_tsline1.outdata_aclr = "OFF",
video_tsline1.outdata_reg = "UNREGISTERED",
video_tsline1.rdaddress_aclr = "OFF",
video_tsline1.rdaddress_reg = "INCLOCK",
video_tsline1.rdcontrol_aclr = "OFF",
video_tsline1.rdcontrol_reg = "UNREGISTERED",
video_tsline1.width = 8,
video_tsline1.widthad = 9,
video_tsline1.wraddress_aclr = "OFF",
video_tsline1.wraddress_reg = "INCLOCK",
video_tsline1.wrcontrol_aclr = "OFF",
video_tsline1.wrcontrol_reg = "INCLOCK";
*/
// 2 lines * 512 pix * 8 bit (10x8) - used for VGA doubler
dpram #(.ADDRWIDTH(10)) video_vmem
(
.clock (clk),
.address_a (vga_cnt_in),
.data_a (vplex),
.wren_a (c3),
.address_b (vga_cnt_out),
.q_b (vgaplex)
);
/*
altdpram video_vmem
(
.inclock (clk),
.outclock (clk),
.wren (c3),
.data (vplex),
.rdaddress (vga_cnt_out),
.wraddress (vga_cnt_in),
.q (vgaplex),
.aclr (1'b0),
.byteena (1'b1),
.inclocken (1'b1),
.outclocken (1'b1),
.rdaddressstall (1'b0),
.rden (1'b1),
.wraddressstall (1'b0)
);
defparam
video_vmem.indata_aclr = "OFF",
video_vmem.indata_reg = "INCLOCK",
video_vmem.intended_device_family = "ACEX1K",
video_vmem.lpm_type = "altdpram",
video_vmem.outdata_aclr = "OFF",
video_vmem.outdata_reg = "OUTCLOCK",
video_vmem.rdaddress_aclr = "OFF",
video_vmem.rdaddress_reg = "INCLOCK",
video_vmem.rdcontrol_aclr = "OFF",
video_vmem.rdcontrol_reg = "UNREGISTERED",
video_vmem.width = 8,
video_vmem.widthad = 10,
video_vmem.wraddress_aclr = "OFF",
video_vmem.wraddress_reg = "INCLOCK",
video_vmem.wrcontrol_aclr = "OFF",
video_vmem.wrcontrol_reg = "INCLOCK";
*/
endmodule

View File

@ -1,3 +1,4 @@
`include "tune.v"
// This is the Tile Sprite Processing Unit
@ -15,7 +16,6 @@
module video_ts
(
// clocks
input wire clk,
@ -62,136 +62,129 @@ module video_ts
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];
// 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];
// 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;
// Layer selectors control
reg [LAYERS-1:0] layer_active;
reg [LAYERS-1:0] layer_skip;
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];
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)
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)
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};
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};
reg [LAYERS-1:0] layer_skip;
always @(posedge clk)
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;
// --- Tile map prefetch ---
// 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];
reg [4:0] tm_x;
// internal layers control
wire tm_end = tm_x == (t1_en ? 5'd16 : 5'd8);
wire tm_next = dram_next && tmap;
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]);
reg [1:0] m_layer;
always @(posedge clk)
// 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
reg [4:0] tm_x;
always @(posedge clk)
// 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;
// --- Tiles ---
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];
// 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 [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};
// 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};
// 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];
// 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)
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
// TMBUF control
// condition write to tx write to tm_valid
// t_layer_start 0 TM_PRE_VALID
// tm_pre_valid tx+1 TM_VALID
@ -199,28 +192,27 @@ always @(posedge clk)
// tile_go tx+1 TM_VALID
// tile_wait tx-1 TM_PRE_VALID
localparam TM_PRE_VALID = 2'b01;
localparam TM_VALID = 2'b10;
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);
reg [1:0] tm_valid_r;
wire tm_valid = tm_valid_r[1];
wire tm_pre_valid = tm_valid_r[0];
wire tm_pre_valid = tm_valid_r[0];
wire tm_valid = tm_valid_r[1];
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;
reg [1:0] tm_valid_r;
always @(posedge clk)
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)
always @(posedge clk)
if (t_layer_start)
tx <= 6'd0;
else if (tm_pre_valid || tile_skip || tile_go)
@ -228,50 +220,60 @@ always @(posedge clk)
else if (tile_wait)
tx <= tx - 6'd1;
// tile Y geometry
wire [4:0] t_line = line[4:0] + ty_offs;
// tile Y geometry
assign t_line = line[4:0] + ty_offs;
// --- Sprites ---
// sprite descriptor fields
// R0
wire [15:0] sfile_rdata;
// --- Sprites ---
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];
// 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
wire [5:0] sprites_addr = s_tnum[5:0];
// 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
reg sprites_last_r;
reg [2:0] s_layer;
reg [7:0] sreg;
reg [4:0] sr_valid;
reg s_leap_r;
// 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;
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 sprites_last_r;
always @(posedge clk) sprites_last_r <= sreg == 8'd255;
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;
reg [2:0] s_layer;
always @(posedge clk)
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
// 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
@ -283,24 +285,16 @@ always @(posedge clk)
// 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;
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;
assign sprite_go = sr2_valid && sprites && tsr_rdy; // a kick to renderer
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)
always @(posedge clk)
if (start)
sr_valid <= SR0_PRE_VALID;
else if (sprites_last)
@ -316,8 +310,7 @@ always @(posedge clk)
else if (sprite_go)
sr_valid <= SR0_PRE_VALID;
reg [7:0] sreg;
always @(posedge clk)
always @(posedge clk)
if (start)
sreg <= 8'd0;
else if (sr0_pre_valid)
@ -327,11 +320,23 @@ always @(posedge clk)
else if (sr1_pre_valid || sprite_go)
sreg <= sreg + 8'd1;
// SFile control
reg [5:0] s_bmline_offset_r;
reg s_leap_r;
// sprite Y geometry
reg [5:0] s_bmline_offset_r;
always @(posedge clk) begin
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;
@ -344,40 +349,111 @@ always @(posedge clk) begin
sprites_xs <= s_xsz;
sprites_xf <= s_xflp;
end
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
(
// 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)
);
// 4 buffers * 2 tile-planes * 64 tiles * 16 bits (9x16) - used to prefetch tiles
// (2 altdprams)
wire [15:0] tmb_rdata;
dpram #(.DATAWIDTH(16), .ADDRWIDTH(9)) video_tmbuf
(
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,3 +1,4 @@
`include "tune.v"
// This module renders pixels into TS-line for tiles/sprites
// Task execution is initiated by 'tsr_go' (one 'clk' period strobe).
@ -47,27 +48,40 @@ module video_ts_render
input wire dram_next
);
// renderer mux
reg [15:0] data;
reg [3:0] pal_r;
reg [2:0] pix_cnt;
// DRAM request
assign dram_req = tsr_go || !mem_rdy;
wire [3:0] pix_m[0:3];
wire [3:0] pix = pix_m[pix_cnt[1:0]];
assign ts_wdata = {pal_r, pix};
assign pix_m[0] = data[7:4];
assign pix_m[1] = data[3:0];
assign pix_m[2] = data[15:12];
assign pix_m[3] = data[11:8];
// DRAM addressing
assign dram_addr = tsr_go ? addr_in : addr_next;
wire [20:0] addr_in = {addr_offset, addr, 1'b0};
wire [13:0] addr_offset = {page[7:3], line};
wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
// as renderer can't move outside the single bitmap line, only 7 bits are processed
// DRAM request
assign dram_req = tsr_go || !mem_rdy;
reg [20:0] addr_reg;
always @(posedge clk) addr_reg <= dram_addr;
// DRAM addressing
reg [20:0] addr_reg;
wire [13:0] addr_offset = {page[7:3], line};
wire [20:0] addr_in = {addr_offset, addr, 1'b0};
wire [20:0] addr_next = {addr_reg[20:7], addr_reg[6:0] + dram_next};
assign dram_addr = tsr_go ? addr_in : addr_next;
// DRAM cycles counter
assign mem_rdy = cyc[4];
always @(posedge clk)
addr_reg <= dram_addr;
reg [4:0] cyc;
always @(posedge clk)
// DRAM cycles counter
reg [4:0] cyc;
assign mem_rdy = cyc[4];
always @(posedge clk or posedge reset)
if (reset)
cyc <= 5'b10000;
else if (tsr_go)
@ -75,29 +89,26 @@ always @(posedge clk)
else if (dram_pre_next)
cyc <= cyc - 5'd1;
// DRAM data fetching
always @(posedge clk)
if (dram_next)
data <= dram_rdata;
// DRAM data fetching
reg [15:0] data;
always @(posedge clk) if (dram_next) data <= dram_rdata;
// pixel render counter
wire render_on = !pix_cnt[2];
assign ts_we = render_on && |pix; // write signal for TS-line
// pixel render counter
assign ts_we = render_on && |pix; // write signal for TS-line
wire render_on = !cnt[2];
reg [2:0] cnt;
always @(posedge clk)
always @(posedge clk or posedge reset)
if (reset)
cnt <= 3'b100;
pix_cnt <= 3'b100;
else if (dram_next)
cnt <= 3'b000;
pix_cnt <= 3'b000;
else if (render_on)
cnt <= cnt + 3'd1;
pix_cnt <= pix_cnt + 3'd1;
// renderer reload
reg tsr_rld;
always @(posedge clk)
// renderer reload
reg tsr_rld;
always @(posedge clk or posedge reset)
if (reset)
tsr_rld <= 1'b0;
else if (tsr_go)
@ -105,12 +116,12 @@ always @(posedge clk)
else if (dram_next)
tsr_rld <= 1'b0;
// delayed values
reg [8:0] x_coord_d;
reg [3:0] pal_d;
reg flip_d;
// delayed values
reg [8:0] x_coord_d;
reg [3:0] pal_d;
reg flip_d;
always @(posedge clk)
always @(posedge clk)
if (tsr_go)
begin
x_coord_d <= x_coord + (flip ? {x_size, 3'b111} : 6'd0);
@ -119,32 +130,21 @@ always @(posedge clk)
end
// TS-line address
wire [8:0] ts_waddr_mx = tsr_rld_stb ? x_coord_d : (render_on ? x_next : ts_waddr);
wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
wire tsr_rld_stb = tsr_rld && dram_next;
// TS-line address
reg flip_r;
always @(posedge clk) ts_waddr <= ts_waddr_mx;
wire [8:0] x_next = ts_waddr + {{8{flip_r}}, 1'b1};
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);
reg [3:0] pal_r;
reg flip_r;
always @(posedge clk)
always @(posedge clk)
ts_waddr <= ts_waddr_mx;
always @(posedge clk)
if (tsr_rld_stb)
begin
pal_r <= pal_d;
flip_r <= flip_d;
end
// renderer mux
assign ts_wdata = {pal_r, pix};
wire [3:0] pix = pix_m[cnt[1:0]];
wire [3:0] pix_m[0:3];
assign pix_m[0] = data[7:4];
assign pix_m[1] = data[3:0];
assign pix_m[2] = data[15:12];
assign pix_m[3] = data[11:8];
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