mirror of
https://github.com/mist-devel/mist-board.git
synced 2026-02-06 08:04:41 +00:00
541 lines
17 KiB
Verilog
541 lines
17 KiB
Verilog
//
|
|
// ql.v - Sinclair QL for the MiST
|
|
//
|
|
// https://github.com/mist-devel
|
|
//
|
|
// Copyright (c) 2015 Till Harbaum <till@harbaum.org>
|
|
//
|
|
// This source file is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This source file is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
module ql (
|
|
input [1:0] CLOCK_27,
|
|
|
|
// LED outputs
|
|
output LED, // LED Yellow
|
|
|
|
// SDRAM interface
|
|
inout [15:0] SDRAM_DQ, // SDRAM Data bus 16 Bits
|
|
output [12:0] SDRAM_A, // SDRAM Address bus 13 Bits
|
|
output SDRAM_DQML, // SDRAM Low-byte Data Mask
|
|
output SDRAM_DQMH, // SDRAM High-byte Data Mask
|
|
output SDRAM_nWE, // SDRAM Write Enable
|
|
output SDRAM_nCAS, // SDRAM Column Address Strobe
|
|
output SDRAM_nRAS, // SDRAM Row Address Strobe
|
|
output SDRAM_nCS, // SDRAM Chip Select
|
|
output [1:0] SDRAM_BA, // SDRAM Bank Address
|
|
output SDRAM_CLK, // SDRAM Clock
|
|
output SDRAM_CKE, // SDRAM Clock Enable
|
|
|
|
// SPI interface to arm io controller
|
|
output SPI_DO,
|
|
input SPI_DI,
|
|
input SPI_SCK,
|
|
input SPI_SS2,
|
|
input SPI_SS3,
|
|
input SPI_SS4,
|
|
input CONF_DATA0,
|
|
|
|
output AUDIO_L, // sigma-delta DAC output left
|
|
output AUDIO_R, // sigma-delta DAC output right
|
|
|
|
output VGA_HS,
|
|
output VGA_VS,
|
|
output [5:0] VGA_R,
|
|
output [5:0] VGA_G,
|
|
output [5:0] VGA_B
|
|
);
|
|
|
|
// -------------------------------------------------------------------------
|
|
// ------------------------------ user_io ----------------------------------
|
|
// -------------------------------------------------------------------------
|
|
|
|
// user_io implements a connection to the io controller and receives various
|
|
// kind of user input from there (keyboard, buttons, mouse). It is also used
|
|
// by the fake SD card to exchange data with the real sd card connected to the
|
|
// io controller
|
|
|
|
// the configuration string is returned to the io controller to allow
|
|
// it to control the menu on the OSD
|
|
parameter CONF_STR = {
|
|
"QL;;",
|
|
"F1,MDV;",
|
|
"O2,MDV direction,normal,reverse;",
|
|
"O3,RAM,128k,640k;",
|
|
"O4,Video mode,PAL,NTSC;",
|
|
"O5,Scanlines,Off,On;",
|
|
"T6,Reset"
|
|
};
|
|
|
|
parameter CONF_STR_LEN = 4+7+32+17+23+20+8;
|
|
|
|
// the status register is controlled by the on screen display (OSD)
|
|
wire [7:0] status;
|
|
wire tv15khz;
|
|
wire [1:0] buttons;
|
|
|
|
wire [7:0] js0, js1;
|
|
|
|
wire ps2_kbd_clk, ps2_kbd_data;
|
|
wire ps2_mouse_clk, ps2_mouse_data;
|
|
|
|
// generate ps2_clock
|
|
wire ps2_clock = ps2_clk_div[6]; // ~20khz
|
|
reg [6:0] ps2_clk_div;
|
|
always @(posedge clk2)
|
|
ps2_clk_div <= ps2_clk_div + 7'd1;
|
|
|
|
// include user_io module for arm controller communication
|
|
user_io #(.STRLEN(CONF_STR_LEN)) user_io (
|
|
.conf_str ( CONF_STR ),
|
|
|
|
.SPI_CLK ( SPI_SCK ),
|
|
.SPI_SS_IO ( CONF_DATA0 ),
|
|
.SPI_MISO ( SPI_DO ),
|
|
.SPI_MOSI ( SPI_DI ),
|
|
|
|
.scandoubler_disable ( tv15khz ),
|
|
.buttons ( buttons ),
|
|
|
|
.joystick_0 ( js0 ),
|
|
.joystick_1 ( js1 ),
|
|
|
|
// ps2 interface
|
|
.ps2_clk ( ps2_clock ),
|
|
.ps2_kbd_clk ( ps2_kbd_clk ),
|
|
.ps2_kbd_data ( ps2_kbd_data ),
|
|
.ps2_mouse_clk ( ps2_mouse_clk ),
|
|
.ps2_mouse_data ( ps2_mouse_data ),
|
|
|
|
.status ( status ),
|
|
|
|
// interface to embedded legacy sd card wrapper
|
|
.sd_lba ( sd_lba ),
|
|
.sd_rd ( sd_rd ),
|
|
.sd_wr ( sd_wr ),
|
|
.sd_ack ( sd_ack ),
|
|
.sd_conf ( sd_conf ),
|
|
.sd_sdhc ( sd_sdhc ),
|
|
.sd_dout ( sd_dout ),
|
|
.sd_dout_strobe (sd_dout_strobe ),
|
|
.sd_din ( sd_din ),
|
|
.sd_din_strobe ( sd_din_strobe )
|
|
);
|
|
|
|
// -------------------------------------------------------------------------
|
|
// ---------------- fake sd card for use with ql-sd ------------------------
|
|
// -------------------------------------------------------------------------
|
|
|
|
// conections between user_io (implementing the SPIU communication
|
|
// to the io controller) and the legacy
|
|
wire [31:0] sd_lba;
|
|
wire sd_rd;
|
|
wire sd_wr;
|
|
wire sd_ack;
|
|
wire sd_conf;
|
|
wire sd_sdhc;
|
|
wire [7:0] sd_dout;
|
|
wire sd_dout_strobe;
|
|
wire [7:0] sd_din;
|
|
wire sd_din_strobe;
|
|
|
|
sd_card sd_card (
|
|
// connection to io controller
|
|
.io_lba ( sd_lba ),
|
|
.io_rd ( sd_rd ),
|
|
.io_wr ( sd_wr ),
|
|
.io_ack ( sd_ack ),
|
|
.io_conf ( sd_conf ),
|
|
.io_sdhc ( sd_sdhc ),
|
|
.io_din ( sd_dout ),
|
|
.io_din_strobe ( sd_dout_strobe ),
|
|
.io_dout ( sd_din ),
|
|
.io_dout_strobe( sd_din_strobe ),
|
|
|
|
.allow_sdhc ( 1'b1 ), // QLSD supports SDHC
|
|
|
|
// connection to local CPU
|
|
.sd_cs ( sd_cs ),
|
|
.sd_sck ( sd_sck ),
|
|
.sd_sdi ( sd_sdi ),
|
|
.sd_sdo ( sd_sdo )
|
|
);
|
|
|
|
wire qlsd_rd = cpu_rom && (cpu_addr[15:0] == 16'hfee4); // only one register actually returns data
|
|
wire [7:0] qlsd_dout;
|
|
wire sd_cs, sd_sck, sd_sdi, sd_sdo;
|
|
|
|
qlromext qlromext (
|
|
.clk ( CLOCK_27[0] ), // fastest we can offer
|
|
.clk_bus ( clk2 ),
|
|
.romoel ( !(cpu_rom && cpu_cycle) ),
|
|
.a ( cpu_addr[15:0] ),
|
|
.d ( qlsd_dout ),
|
|
.sd_do ( sd_sdo ),
|
|
.sd_cs1l ( sd_cs ),
|
|
.sd_clk ( sd_sck ),
|
|
.sd_di ( sd_sdi ),
|
|
.io2 ( 1'b0 )
|
|
);
|
|
|
|
// -------------------------------------------------------------------------
|
|
// ---------------- interface to the external sdram ------------------------
|
|
// -------------------------------------------------------------------------
|
|
|
|
// SDRAM control signals
|
|
assign SDRAM_CKE = 1'b1;
|
|
|
|
// CPU and data_io share the same bus cycle. Thus the CPU cannot run while
|
|
// (ROM) data is being downloaded which wouldn't make any sense, anyway
|
|
// during ROM download data_io writes the ram. Otherwise the CPU
|
|
wire [24:0] sys_addr = dio_download?dio_addr[24:0]:{ 6'b000000, cpu_addr[19:1]};
|
|
wire [1:0] sys_ds = dio_download?2'b11:~cpu_ds;
|
|
wire [15:0] sys_dout = dio_download?dio_data:cpu_dout;
|
|
wire sys_wr = dio_download?dio_write:(cpu_wr && cpu_ram);
|
|
wire sys_oe = dio_download?1'b0:(cpu_rd && cpu_mem);
|
|
|
|
// microdrive emulation and video share the video cycle time slot
|
|
wire [24:0] video_cycle_addr = mdv_read?mdv_addr:{6'd0, video_addr};
|
|
wire video_cycle_rd = mdv_read?1'b1:video_rd;
|
|
|
|
// video and CPU/data_io time share the sdram bus
|
|
wire [24:0] sdram_addr = video_cycle?video_cycle_addr:sys_addr;
|
|
wire sdram_wr = video_cycle?1'b0:sys_wr;
|
|
wire sdram_oe = video_cycle?video_cycle_rd:sys_oe;
|
|
wire [1:0] sdram_ds = video_cycle?2'b11:sys_ds;
|
|
wire [15:0] sdram_dout;
|
|
wire [15:0] sdram_din = sys_dout;
|
|
|
|
sdram sdram (
|
|
// interface to the MT48LC16M16 chip
|
|
.sd_data ( SDRAM_DQ ),
|
|
.sd_addr ( SDRAM_A ),
|
|
.sd_dqm ( {SDRAM_DQMH, SDRAM_DQML} ),
|
|
.sd_cs ( SDRAM_nCS ),
|
|
.sd_ba ( SDRAM_BA ),
|
|
.sd_we ( SDRAM_nWE ),
|
|
.sd_ras ( SDRAM_nRAS ),
|
|
.sd_cas ( SDRAM_nCAS ),
|
|
|
|
// system interface
|
|
.clk ( clk21 ),
|
|
.clkref ( clk2 ),
|
|
.init ( !pll_locked ),
|
|
|
|
// cpu interface
|
|
.din ( sdram_din ),
|
|
.addr ( sdram_addr ),
|
|
.we ( sdram_wr ),
|
|
.oe ( sdram_oe ),
|
|
.ds ( sdram_ds ),
|
|
.dout ( sdram_dout )
|
|
);
|
|
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// ------------------------------------- data io -----------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
wire dio_download;
|
|
wire [4:0] dio_index;
|
|
wire [24:0] dio_addr;
|
|
wire [15:0] dio_data;
|
|
wire dio_write;
|
|
|
|
// include ROM download helper
|
|
// this receives a byte stream from the arm io controller via spi and
|
|
// writes it into sdram
|
|
data_io data_io (
|
|
// io controller spi interface
|
|
.sck ( SPI_SCK ),
|
|
.ss ( SPI_SS2 ),
|
|
.sdi ( SPI_DI ),
|
|
|
|
.downloading ( dio_download ), // signal indicating an active rom download
|
|
.index ( dio_index ),
|
|
|
|
// external ram interface
|
|
.clk ( cpu_cycle ),
|
|
.wr ( dio_write ),
|
|
.addr ( dio_addr ),
|
|
.data ( dio_data )
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// -------------------------------------- video ------------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
wire [5:0] video_r, video_g, video_b;
|
|
wire video_hs, video_vs;
|
|
|
|
wire [18:0] video_addr;
|
|
wire video_rd;
|
|
|
|
// the zx8301 has only one write-only register at $18063
|
|
wire zx8301_cs = cpu_cycle && cpu_io &&
|
|
({cpu_addr[6:5], cpu_addr[1]} == 3'b111)&& cpu_wr && !cpu_ds[0];
|
|
|
|
zx8301 zx8301 (
|
|
.reset ( reset ),
|
|
.clk_vga ( clk21 ),
|
|
.clk_video ( clk10 ),
|
|
.video_cycle ( video_cycle ),
|
|
|
|
.ntsc ( status[4] ),
|
|
.scandoubler ( !tv15khz ),
|
|
.scanlines ( status[5] ),
|
|
|
|
.clk_bus ( clk2 ),
|
|
.cpu_cs ( zx8301_cs ),
|
|
.cpu_data ( cpu_dout[7:0] ),
|
|
|
|
.mdv_men ( mdv_men ),
|
|
|
|
.addr ( video_addr ),
|
|
.din ( sdram_dout ),
|
|
.rd ( video_rd ),
|
|
|
|
.hs ( video_hs ),
|
|
.vs ( video_vs ),
|
|
.r ( video_r ),
|
|
.g ( video_g ),
|
|
.b ( video_b )
|
|
);
|
|
|
|
|
|
// csync for tv15khz
|
|
// QLs vsync is positive, QLs hsync is negative
|
|
wire vga_csync = !(!vga_hsync ^ vga_vsync);
|
|
wire vga_hsync, vga_vsync;
|
|
|
|
// TV SCART has csync on hsync pin and "high" on vsync pin
|
|
assign VGA_VS = tv15khz?1'b1:vga_vsync;
|
|
assign VGA_HS = tv15khz?vga_csync:vga_hsync;
|
|
|
|
// tv15hkz has half the pixel rate
|
|
wire osd_clk = tv15khz?clk10:clk21;
|
|
|
|
// include the on screen display
|
|
osd #(12,0,5) osd (
|
|
.pclk ( osd_clk ),
|
|
|
|
// spi for OSD
|
|
.sdi ( SPI_DI ),
|
|
.sck ( SPI_SCK ),
|
|
.ss ( SPI_SS3 ),
|
|
|
|
.red_in ( video_r ),
|
|
.green_in ( video_g ),
|
|
.blue_in ( video_b ),
|
|
.hs_in ( video_hs ),
|
|
.vs_in ( video_vs ),
|
|
|
|
.red_out ( VGA_R ),
|
|
.green_out ( VGA_G ),
|
|
.blue_out ( VGA_B ),
|
|
.hs_out ( vga_hsync ),
|
|
.vs_out ( vga_vsync )
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// -------------------------------------- reset ------------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
wire rom_download = dio_download && (dio_index == 0);
|
|
reg [11:0] reset_cnt;
|
|
wire reset = (reset_cnt != 0);
|
|
always @(posedge clk2) begin
|
|
if(buttons[1] || status[0] || status[6] || !pll_locked || rom_download)
|
|
reset_cnt <= 12'hfff;
|
|
else if(reset_cnt != 0)
|
|
reset_cnt <= reset_cnt - 1;
|
|
end
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// --------------------------------------- IO --------------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
wire zx8302_sel = cpu_cycle && cpu_io && !cpu_addr[6];
|
|
wire [1:0] zx8302_addr = {cpu_addr[5], cpu_addr[1]};
|
|
wire [15:0] zx8302_dout;
|
|
|
|
wire mdv_download = (dio_index == 1) && dio_download;
|
|
wire mdv_men;
|
|
wire mdv_read;
|
|
wire [24:0] mdv_addr;
|
|
|
|
wire audio;
|
|
assign AUDIO_L = audio;
|
|
assign AUDIO_R = audio;
|
|
|
|
zx8302 zx8302 (
|
|
.reset ( reset ),
|
|
.init ( !pll_locked ),
|
|
.clk_sys ( CLOCK_27[0] ),
|
|
.clk ( clk21 ),
|
|
|
|
.xint ( qimi_irq ),
|
|
.ipl ( cpu_ipl ),
|
|
.led ( LED ),
|
|
.audio ( audio ),
|
|
|
|
// CPU connection
|
|
.clk_bus ( clk2 ),
|
|
.cpu_sel ( zx8302_sel ),
|
|
.cpu_wr ( cpu_wr ),
|
|
.cpu_addr ( zx8302_addr ),
|
|
.cpu_ds ( cpu_ds ),
|
|
.cpu_din ( cpu_dout ),
|
|
.cpu_dout ( zx8302_dout ),
|
|
|
|
// joysticks
|
|
.js0 ( js0[4:0] ),
|
|
.js1 ( js1[4:0] ),
|
|
|
|
.ps2_kbd_clk ( ps2_kbd_clk ),
|
|
.ps2_kbd_data ( ps2_kbd_data ),
|
|
|
|
.vs ( video_vs ),
|
|
|
|
// microdrive sdram interface
|
|
.mdv_addr ( mdv_addr ),
|
|
.mdv_din ( sdram_dout ),
|
|
.mdv_read ( mdv_read ),
|
|
.mdv_men ( mdv_men ),
|
|
.video_cycle ( video_cycle ),
|
|
|
|
.mdv_reverse ( status[2] ),
|
|
|
|
.mdv_download ( mdv_download ),
|
|
.mdv_dl_addr ( dio_addr )
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// --------------------------- QIMI compatible mouse -------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
// qimi is at 1bfxx
|
|
wire qimi_sel = cpu_io && (cpu_addr[13:8] == 6'b111111);
|
|
wire [7:0] qimi_data;
|
|
wire qimi_irq;
|
|
|
|
qimi qimi(
|
|
.reset ( reset ),
|
|
.clk ( clk2 ),
|
|
|
|
.cpu_sel ( qimi_sel ),
|
|
.cpu_addr ( { cpu_addr[5], cpu_addr[1] } ),
|
|
.cpu_data ( qimi_data ),
|
|
.irq ( qimi_irq ),
|
|
|
|
.ps2_clk ( ps2_mouse_clk ),
|
|
.ps2_data ( ps2_mouse_data )
|
|
);
|
|
|
|
// ---------------------------------------------------------------------------------
|
|
// -------------------------------------- CPU --------------------------------------
|
|
// ---------------------------------------------------------------------------------
|
|
|
|
// address decoding
|
|
wire cpu_act = cpu_rd || cpu_wr;
|
|
wire cpu_io = cpu_act && ({cpu_addr[19:14], 2'b00} == 8'h18); // internal IO $18000-$1bffff
|
|
wire cpu_bram = cpu_act &&(cpu_addr[19:17] == 3'b001); // 128k RAM at $20000
|
|
wire cpu_xram = cpu_act && status[3] && ((cpu_addr[19:18] == 2'b01) ||
|
|
(cpu_addr[19:18] == 2'b10)); // 512k RAM at $40000 if enabled
|
|
wire cpu_ram = cpu_bram || cpu_xram; // any RAM
|
|
wire cpu_rom = cpu_act && (cpu_addr[19:16] == 4'h0); // 64k ROM at $0
|
|
wire cpu_mem = cpu_ram || cpu_rom; // any memory mapped to sdram
|
|
|
|
wire [15:0] io_dout =
|
|
qimi_sel?{qimi_data, qimi_data}:
|
|
(!cpu_addr[6])?zx8302_dout:
|
|
16'h0000;
|
|
|
|
// demultiplex the various data sources
|
|
wire [15:0] cpu_din =
|
|
qlsd_rd?{qlsd_dout, qlsd_dout}: // qlsd maps into rom area
|
|
cpu_mem?sdram_dout:
|
|
cpu_io?io_dout:
|
|
16'hffff;
|
|
|
|
wire [31:0] cpu_addr;
|
|
wire [1:0] cpu_ds;
|
|
wire [15:0] cpu_dout;
|
|
wire [1:0] cpu_ipl;
|
|
wire cpu_rw;
|
|
wire [1:0] cpu_busstate;
|
|
wire cpu_rd = (cpu_busstate == 2'b00) || (cpu_busstate == 2'b10);
|
|
wire cpu_wr = (cpu_busstate == 2'b11) && !cpu_rw;
|
|
wire cpu_idle = (cpu_busstate == 2'b01);
|
|
|
|
reg cpu_enable;
|
|
always @(negedge clk2)
|
|
cpu_enable <= (cpu_cycle && !dio_download) || cpu_idle;
|
|
|
|
TG68KdotC_Kernel #(0,0,0,0,0,0) tg68k (
|
|
.clk ( clk2 ),
|
|
.nReset ( ~reset ),
|
|
.clkena_in ( cpu_enable ),
|
|
.data_in ( cpu_din ),
|
|
.IPL ( {cpu_ipl[0], cpu_ipl }), // ipl 0 and 2 are tied together on 68008
|
|
.IPL_autovector ( 1'b1 ),
|
|
.berr ( 1'b0 ),
|
|
.clr_berr ( 1'b0 ),
|
|
.CPU ( 2'b00 ), // 00=68000
|
|
.addr ( cpu_addr ),
|
|
.data_write ( cpu_dout ),
|
|
.nUDS ( cpu_ds[1] ),
|
|
.nLDS ( cpu_ds[0] ),
|
|
.nWr ( cpu_rw ),
|
|
.busstate ( cpu_busstate ), // 00-> fetch code 10->read data 11->write data 01->no memaccess
|
|
.nResetOut ( ),
|
|
.FC ( )
|
|
);
|
|
|
|
// -------------------------------------------------------------------------
|
|
// -------------------------- clock generation -----------------------------
|
|
// -------------------------------------------------------------------------
|
|
|
|
reg clk10; // 10.5 MHz QL pixel clock
|
|
wire clk21;
|
|
always @(posedge clk21)
|
|
clk10 <= !clk10;
|
|
|
|
reg clk5; // 5.25 MHz CPU clock
|
|
always @(posedge clk10)
|
|
clk5 <= !clk5;
|
|
|
|
reg clk2; // 2.625 MHz bus clock
|
|
always @(posedge clk5)
|
|
clk2 <= !clk2;
|
|
|
|
// CPU and Video share the bus
|
|
reg video_cycle;
|
|
wire cpu_cycle = !video_cycle;
|
|
always @(posedge clk2)
|
|
video_cycle <= !video_cycle;
|
|
|
|
wire pll_locked;
|
|
|
|
// A PLL to derive the system clock from the MiSTs 27MHz
|
|
pll pll (
|
|
.inclk0( CLOCK_27[0] ),
|
|
.c0( clk21 ), // 21.000 MHz
|
|
.c1( SDRAM_CLK ), // 21.000 MHz phase shifted
|
|
.locked( pll_locked )
|
|
);
|
|
|
|
|
|
endmodule
|