// part of NeoGS project (c) 2007-2008 NedoPC
//
// ports $00-$3f are in FPGA, $40-$ff are in CPLD
module ports(
din, // NGS z80 cpu DATA BUS inputs
dout, // NGS z80 cpu DATA BUS outputs
busin, // direction of bus: =1 - input, =0 - output
a, // NSG z80 cpu ADDRESS BUS
iorq_n,mreq_n,rd_n,wr_n, // NGS z80 cpu control signals
data_port_input, // data_port input from zxbus module (async)
data_port_output, // data_port output to zxbus module (async to zxbus, sync here)
command_port_input, // command_port input from zxbus (async)
data_bit_input, // data_bit from zxbus module (sync)
command_bit_input, // --//-- (sync)
data_bit_output, // output to zxbus module
command_bit_output,
data_bit_wr, // strobes (positive) to zxbus module, synchronous
command_bit_wr,
mode_8chans, // mode outputs for sound_main module
mode_pan4ch, //
mode_ramro, // mode outputs for memmap module
mode_norom,
mode_pg0, // page registers for memmap module
mode_pg1,
clksel0, // clock select (output from FPGA)
clksel1,
snd_wrtoggle, // toggle to write sound data to sound system memory
snd_datnvol, // whether it's for volume (=0) or for samples (=1)
snd_addr, // address: which channel to be written (0-7)
snd_data, // actual 8-bit data to be written
md_din, // mp3 data interface
md_start,
md_dreq,
md_halfspeed,
mc_ncs, // mp3 control interface
mc_xrst,
mc_dout,
mc_din,
mc_start,
mc_speed,
mc_rdy,
sd_ncs, // SD card interface
sd_dout,
sd_din,
sd_start,
sd_det,
sd_wp,
led, // LED control
led_toggle,
dma_din_modules, // DMA control
//
dma_select_zx,
dma_dout_zx,
//
dma_wrstb,
dma_regsel,
rst_n,
cpu_clock // Z80 CPU clock (clk_fpga on schematics)
);
localparam MPAG = 6'h00;
localparam MPAGEX = 6'h10;
localparam ZXCMD = 6'h01;
localparam ZXDATRD = 6'h02;
localparam ZXDATWR = 6'h03;
localparam ZXSTAT = 6'h04;
localparam CLRCBIT = 6'h05;
localparam VOL1 = 6'h06;
localparam VOL2 = 6'h07;
localparam VOL3 = 6'h08;
localparam VOL4 = 6'h09;
localparam VOL5 = 6'h16;
localparam VOL6 = 6'h17;
localparam VOL7 = 6'h18;
localparam VOL8 = 6'h19;
localparam DAMNPORT1 = 6'h0a;
localparam DAMNPORT2 = 6'h0b;
localparam LEDCTR = 6'h01;
localparam GSCFG0 = 6'h0f;
localparam SCTRL = 6'h11;
localparam SSTAT = 6'h12;
localparam SD_SEND = 6'h13;
localparam SD_READ = 6'h13;
localparam SD_RSTR = 6'h14;
localparam MD_SEND = 6'h14; // same as SD_RSTR!!!
localparam MC_SEND = 6'h15;
localparam MC_READ = 6'h15;
localparam DMA_MOD = 6'h1b; // read/write
localparam DMA_HAD = 6'h1c; // LSB bits 1:0 are 00 // read/write all
localparam DMA_MAD = 6'h1d; // 01
localparam DMA_LAD = 6'h1e; // 10
localparam DMA_CST = 6'h1f; // 11
localparam DMA_PORTS = 6'h1c; // mask for _HAD, _MAD, _LAD and _CST ports, two LSBs must be zero
// FREE PORT ADDRESSES: $0C-$0E, $1A, $20-$3F
// inputs/outputs description
input [7:0] din;
output reg [7:0] dout;
output reg busin; // =1 - dbus ins, =0 - dbus outs
input [15:0] a;
input iorq_n,mreq_n,rd_n,wr_n;
input [7:0] data_port_input;
input [7:0] command_port_input;
output reg [7:0] data_port_output;
input data_bit_input;
input command_bit_input;
output reg data_bit_output;
output reg command_bit_output;
output reg data_bit_wr;
output reg command_bit_wr;
output reg mode_8chans;
output reg mode_pan4ch;
output reg mode_ramro;
output reg mode_norom;
output reg [6:0] mode_pg0;
output reg [6:0] mode_pg1;
output reg clksel0;
output reg clksel1;
output reg snd_wrtoggle;
output reg snd_datnvol;
output reg [2:0] snd_addr;
output reg [7:0] snd_data;
input rst_n;
input cpu_clock;
// SPI interfaces related
// MP3 data interface
output [7:0] md_din; // data to MP3 data SPI interface
output md_start; // start toggle for mp3 data spi
input md_dreq; // data request from mp3 decoder
output reg md_halfspeed;
// MP3 control interface
output reg mc_ncs; // nCS signal
output reg mc_xrst; // xRESET signal
output mc_start; // start toggle
output reg [1:0] mc_speed;
input mc_rdy;
output [7:0] mc_din; // data to send
input [7:0] mc_dout; // received data
// SDcard interface
output reg sd_ncs;
output sd_start;
output [7:0] sd_din;
input [7:0] sd_dout;
input sd_det;
input sd_wp;
// DMA modules control
//
output reg [7:0] dma_din_modules;
//
input [7:0] dma_dout_zx;
output reg dma_select_zx;
//
output reg dma_wrstb;
output reg [1:0] dma_regsel;
// LED control register
output reg led;
input led_toggle;
// internal regs & wires
reg mode_expag; // extended paging mode register
reg port09_bit5;
wire port_enabled; // =1 when port address is in enabled region ($00-$3f)
wire mem_enabled; // =1 when memory mapped sound regs are addressed ($6000-$7FFF)
reg volports_enabled; // when volume ports are addressed (6-9 and $16-$19)
reg iowrn_reg; // registered io write signal (all positive edge!)
reg iordn_reg; // --//--
reg merdn_reg; // --//--
reg port_wr; // synchronous positive write pulse (write from z80 to fpga regs)
reg port_rd; // synchronous positive read pulse (read done from fpga regs to z80)
reg memreg_rd; // when memory-mapped sound regs are read
wire port00_wr; // specific write and read strobes (1 clock cycle long positive)
wire p_ledctr_wr;
wire port02_rd;
wire port03_wr;
wire port05_wrrd;
wire port09_wr;
wire port0a_wrrd;
wire port0b_wrrd;
wire port0f_wr;
wire port10_wr;
// wire p_sstat_rd;
// wire p_sctrl_rd;
wire p_sctrl_wr;
wire p_sdsnd_wr;
// wire p_sdrd_rd;
wire p_sdrst_rd;
wire p_mdsnd_wr;
wire p_mcsnd_wr;
// wire p_mcrd_rd;
wire p_dmamod_wr;
wire p_dmaports_wr;
reg [2:0] volnum; // volume register number from port address
reg [2:0] dma_module_select; // which dma module selected: zero - none selected
localparam DMA_NONE_SELECTED = 3'd0;
localparam DMA_MODULE_ZX = 3'd1;
// localparam DMA_MODULE_... = 3'd2;
reg [7:0] dma_dout_modules; // select in data from different modules
// actual code
//enabled ports
assign port_enabled = ~(a[7] | a[6]); // $00-$3F
//enabled mem
assign mem_enabled = (~a[15]) & a[14] & a[13]; // $6000-$7FFF
// volume ports enabled
always @*
begin
if( a[5:0]==VOL1 ||
a[5:0]==VOL2 ||
a[5:0]==VOL3 ||
a[5:0]==VOL4 ||
a[5:0]==VOL5 ||
a[5:0]==VOL6 ||
a[5:0]==VOL7 ||
a[5:0]==VOL8 )
volports_enabled <= 1'b1;
else
volports_enabled <= 1'b0;
end
//when data bus outputs
always @*
begin
if( port_enabled && (!iorq_n) && (!rd_n) )
busin <= 1'b0; // bus outputs
else
busin <= 1'b1; // bus inputs
end
// rd/wr/iorq syncing in and pulses
always @(posedge cpu_clock)
begin
iowrn_reg <= iorq_n | wr_n;
iordn_reg <= iorq_n | rd_n;
if( port_enabled && (!iorq_n) && (!wr_n) && iowrn_reg )
port_wr <= 1'b1;
else
port_wr <= 1'b0;
if( port_enabled && (!iorq_n) && (!rd_n) && iordn_reg )
port_rd <= 1'b1;
else
port_rd <= 1'b0;
end
// mreq syncing and mem read pulse
always @(negedge cpu_clock)
begin
merdn_reg <= mreq_n | rd_n;
if( mem_enabled && (!mreq_n) && (!rd_n) && merdn_reg )
memreg_rd <= 1'b1;
else
memreg_rd <= 1'b0;
end
// specific ports strobes
assign port00_wr = ( a[5:0]==MPAG && port_wr );
assign port02_rd = ( a[5:0]==ZXDATRD && port_rd );
assign port03_wr = ( a[5:0]==ZXDATWR && port_wr );
assign port05_wrrd = ( a[5:0]==CLRCBIT && (port_wr||port_rd) );
assign port09_wr = ( a[5:0]==VOL4 && port_wr );
assign port0a_wrrd = ( a[5:0]==DAMNPORT1 && (port_wr||port_rd) );
assign port0b_wrrd = ( a[5:0]==DAMNPORT2 && (port_wr||port_rd) );
assign port0f_wr = ( a[5:0]==GSCFG0 && port_wr );
assign port10_wr = ( a[5:0]==MPAGEX && port_wr );
// assign p_sctrl_rd = ( a[5:0]==SCTRL && port_rd );
assign p_sctrl_wr = ( a[5:0]==SCTRL && port_wr );
// assign p_sstat_rd = ( a[5:0]==SSTAT && port_rd );
assign p_sdsnd_wr = ( a[5:0]==SD_SEND && port_wr );
// assign p_sdrd_rd = ( a[5:0]==SD_READ && port_rd );
assign p_sdrst_rd = ( a[5:0]==SD_RSTR && port_rd );
assign p_mdsnd_wr = ( a[5:0]==MD_SEND && port_wr );
assign p_mcsnd_wr = ( a[5:0]==MC_SEND && port_wr );
// assign p_mcrd_rd = ( a[5:0]==MC_READ && port_rd );
assign p_ledctr_wr = ( a[5:0]==LEDCTR && port_wr );
assign p_dmamod_wr = ( a[5:0]==DMA_MOD && port_wr );
assign p_dmaports_wr = ( {a[5:2],2'b00}==DMA_PORTS && port_wr );
// read from fpga to Z80
always @*
begin
case( a[5:0] )
ZXCMD: // command register
dout <= command_port_input;
ZXDATRD: // data register
dout <= data_port_input;
ZXSTAT: // status bits
dout <= { data_bit_input, 6'bXXXXXX, command_bit_input };
GSCFG0: // config register #0F
dout <= { 1'b0, mode_pan4ch, clksel1, clksel0, mode_expag, mode_8chans, mode_ramro, mode_norom };
SSTAT:
dout <= { 4'd0, mc_rdy, sd_wp, sd_det, md_dreq };
SCTRL:
dout <= { 2'd0, mc_speed[1], md_halfspeed, mc_speed[0], mc_xrst, mc_ncs, sd_ncs };
SD_READ:
dout <= sd_dout;
SD_RSTR:
dout <= sd_dout;
MC_READ:
dout <= mc_dout;
DMA_MOD:
dout <= { 5'd0, dma_module_select };
DMA_HAD:
dout <= dma_dout_modules;
DMA_MAD:
dout <= dma_dout_modules;
DMA_LAD:
dout <= dma_dout_modules;
DMA_CST:
dout <= dma_dout_modules;
default:
dout <= 8'bXXXXXXXX;
endcase
end
// write to $00 and $10 ports ++
always @(posedge cpu_clock)
begin
if( port00_wr==1'b1 ) // port 00
begin
if( mode_expag==1'b0 ) // normal paging
mode_pg0[6:0] <= { din[5:0], 1'b0 };
else // extended paging
mode_pg0[6:0] <= { din[5:0], din[7] };
end
if( mode_expag==1'b0 && port00_wr==1'b1 ) // port 10 (when in normal mode, part of port 00)
mode_pg1[6:0] <= { din[5:0], 1'b1 };
else if( mode_expag==1'b1 && port10_wr==1'b1 )
mode_pg1[6:0] <= { din[5:0], din[7] };
end
// port $03 write ++
always @(posedge cpu_clock)
begin
if( port03_wr==1'b1 )
data_port_output <= din;
end
// port $09 bit tracing
always @(posedge cpu_clock)
begin
if( port09_wr==1'b1 )
port09_bit5 <= din[5];
end
// write and reset of port $0F ++
always @(posedge cpu_clock,negedge rst_n)
begin
if( rst_n==1'b0 ) // reset!
{ mode_pan4ch, clksel1, clksel0, mode_expag, mode_8chans, mode_ramro, mode_norom } <= 7'b0110000;
else // write to port
begin
if( port0f_wr == 1'b1 )
begin
{ mode_pan4ch, clksel1, clksel0, mode_expag, mode_8chans, mode_ramro, mode_norom } <= din[6:0];
end
end
end
// data bit handling
always @*
begin
case( {port02_rd,port03_wr,port0a_wrrd} )
3'b100:
begin
data_bit_output <= 1'b0;
data_bit_wr <= 1'b1;
end
3'b010:
begin
data_bit_output <= 1'b1; // ++
data_bit_wr <= 1'b1;
end
3'b001:
begin
data_bit_output <= ~mode_pg0[0];
data_bit_wr <= 1'b1;
end
default:
begin
data_bit_output <= 1'bX;
data_bit_wr <= 1'b0;
end
endcase
end
// command bit handling
always @*
begin
casex( {port05_wrrd,port0b_wrrd} )
2'b10:
begin
command_bit_output <= 1'b0;
command_bit_wr <= 1'b1;
end
2'b01:
begin
command_bit_output <= port09_bit5;
command_bit_wr <= 1'b1;
end
default:
begin
command_bit_output <= 1'bX;
command_bit_wr <= 1'b0;
end
endcase
end
// handle data going to sound module (volume and samples values)
always @*
begin
case( a[5:0] ) // port addresses to volume register numbers
VOL1:
volnum <= 3'd0;
VOL2:
volnum <= 3'd1;
VOL3:
volnum <= 3'd2;
VOL4:
volnum <= 3'd3;
VOL5:
volnum <= 3'd4;
VOL6:
volnum <= 3'd5;
VOL7:
volnum <= 3'd6;
VOL8:
volnum <= 3'd7;
default:
volnum <= 3'bXXX;
endcase
end
// handling itself (sending data to sound module)
always @(posedge cpu_clock)
begin
if( memreg_rd ) // memory read - sample data write
begin
snd_wrtoggle <= ~snd_wrtoggle;
snd_datnvol <= 1'b1; // sample data
if( !mode_8chans ) // 4 channel mode
snd_addr <= { 1'b0, a[9:8] };
else // 8 channel mode
snd_addr <= a[10:8];
snd_data <= din;
end
else if( volports_enabled && port_wr )
begin
snd_wrtoggle <= ~snd_wrtoggle;
snd_datnvol <= 1'b0; // volume data
snd_addr <= volnum;
snd_data <= din;
end
end
//SPI (mp3, SD) interfaces
assign sd_din = (a[5:0]==SD_RSTR) ? 8'hFF : din;
assign mc_din = din;
assign md_din = din;
assign sd_start = p_sdsnd_wr | p_sdrst_rd;
assign mc_start = p_mcsnd_wr;
assign md_start = p_mdsnd_wr;
always @(posedge cpu_clock, negedge rst_n)
begin
if( !rst_n ) // async reset
begin
md_halfspeed <= 1'b0;
mc_speed <= 2'b01;
mc_xrst <= 1'b0;
mc_ncs <= 1'b1;
sd_ncs <= 1'b1;
end
else // clock
begin
if( p_sctrl_wr )
begin
if( din[0] )
sd_ncs <= din[7];
if( din[1] )
mc_ncs <= din[7];
if( din[2] )
mc_xrst <= din[7];
if( din[3] )
mc_speed[0] <= din[7];
if( din[4] )
md_halfspeed <= din[7];
if( din[5] )
mc_speed[1] <= din[7];
end
end
end
// LED control
always @(posedge cpu_clock, negedge rst_n)
begin
if( !rst_n )
led <= 1'b0;
else
begin
if( p_ledctr_wr )
led <= din[0];
else if( led_toggle )
led <= ~led;
end
end
// DMA control
//
always @(posedge cpu_clock, negedge rst_n) // selection of modules
begin
if( !rst_n )
dma_module_select <= DMA_NONE_SELECTED;
else if( p_dmamod_wr )
dma_module_select <= din[2:0];
end
//
always @* dma_din_modules = din; // translate Z80 bus out to all DMA modules
//
always @* // select modules by individual signals
begin
dma_select_zx = 1'b0;
//dma_select_... = 1'b0;
case( dma_module_select )
DMA_MODULE_ZX:
dma_select_zx = 1'b1;
//DMA_MODULE_...:
// dma_select_... = 1'b1;
endcase
end
//
always @* dma_wrstb = p_dmaports_wr; // translate dma write strobe
//
always @* dma_regsel = a[1:0];
//
always @* // route data from modules to the common module bus
begin
case( dma_regsel )
DMA_MODULE_ZX:
dma_dout_modules <= dma_dout_zx;
//DMA_MODULE_...:
// dma_dout_modules <= dma_dout_...;
default:
dma_dout_modules <= 8'bxxxxxxxx;
endcase
end
endmodule