mirror of
https://github.com/mist-devel/mist-board.git
synced 2026-01-28 20:40:43 +00:00
[ATARI ST] Modular video subsystem, scandoubler made optional
This commit is contained in:
@@ -323,6 +323,9 @@ set_global_assignment -name VERILOG_FILE clock.v
|
||||
set_global_assignment -name VERILOG_FILE mist_top.v
|
||||
set_global_assignment -name VERILOG_FILE user_io.v
|
||||
set_global_assignment -name VERILOG_FILE video.v
|
||||
set_global_assignment -name VERILOG_FILE shifter.v
|
||||
set_global_assignment -name VERILOG_FILE scandoubler.v
|
||||
set_global_assignment -name VERILOG_FILE sync_adjust.v
|
||||
set_global_assignment -name VERILOG_FILE viking.v
|
||||
set_global_assignment -name VERILOG_FILE video_modes.v
|
||||
set_global_assignment -name VERILOG_FILE io_fifo.v
|
||||
|
||||
@@ -240,7 +240,7 @@ wire [15:0] io_data_out = vreg_data_out | dma_data_out | blitter_data_out |
|
||||
|
||||
wire init = ~pll_locked;
|
||||
|
||||
/* -------------------------- Viking video card -------------------- */
|
||||
/* -------------------------- Video subsystem -------------------- */
|
||||
|
||||
// viking/sm194 is enabled and max 8MB memory may be enabled. In steroids mode
|
||||
// video memory is moved to $e80000 and all stram up to 14MB may be used
|
||||
@@ -263,79 +263,47 @@ always @(posedge clk_32) begin
|
||||
viking_in_use <= viking_in_use + 8'h01;
|
||||
end
|
||||
|
||||
// video output multiplexer to switch between shifter and viking
|
||||
wire viking_active = (viking_in_use == 8'hff) && !osd_enable;
|
||||
assign VGA_HS = viking_active?viking_hs:shifter_hs;
|
||||
assign VGA_VS = viking_active?viking_vs:shifter_vs;
|
||||
assign VGA_R = viking_active?viking_r:shifter_r;
|
||||
assign VGA_G = viking_active?viking_g:shifter_g;
|
||||
assign VGA_B = viking_active?viking_b:shifter_b;
|
||||
|
||||
wire viking_hs, viking_vs;
|
||||
wire [5:0] viking_r, viking_g, viking_b;
|
||||
|
||||
wire [22:0] viking_address;
|
||||
wire viking_read;
|
||||
|
||||
viking viking (
|
||||
.pclk (clk_128 ), // pixel
|
||||
.bus_cycle (bus_cycle ),
|
||||
|
||||
// memory interface
|
||||
.himem (steroids ),
|
||||
.bclk (clk_8 ), // system bus clock = 8Mhz
|
||||
.addr (viking_address ),
|
||||
.data (ram_data_out_64),
|
||||
.read (viking_read ),
|
||||
|
||||
// video output
|
||||
.hs (viking_hs ),
|
||||
.vs (viking_vs ),
|
||||
.r (viking_r ),
|
||||
.g (viking_g ),
|
||||
.b (viking_b )
|
||||
);
|
||||
|
||||
wire osd_enable;
|
||||
wire shifter_hs, shifter_vs;
|
||||
wire [5:0] shifter_r, shifter_g, shifter_b;
|
||||
wire viking_active = (viking_in_use == 8'hff);
|
||||
|
||||
video video (
|
||||
.clk (clk_32 ),
|
||||
.clk_128 (clk_128 ),
|
||||
.clk_32 (clk_32 ),
|
||||
.bus_cycle (bus_cycle ),
|
||||
|
||||
// spi for OSD
|
||||
.sdi (SPI_DI ),
|
||||
.sck (SPI_SCK ),
|
||||
.ss (SPI_SS3 ),
|
||||
.osd_enable (osd_enable ),
|
||||
|
||||
// cpu register interface
|
||||
.reg_clk (clk_8 ),
|
||||
.reg_reset (reset ),
|
||||
.reg_din (tg68_dat_out),
|
||||
.reg_sel (vreg_sel ),
|
||||
.reg_addr (tg68_adr[6:1]),
|
||||
.reg_uds (tg68_uds ),
|
||||
.reg_lds (tg68_lds ),
|
||||
.reg_rw (tg68_rw ),
|
||||
.reg_dout (vreg_data_out),
|
||||
.cpu_clk (clk_8 ),
|
||||
.cpu_reset (reset ),
|
||||
.cpu_din (tg68_dat_out),
|
||||
.cpu_sel (vreg_sel ),
|
||||
.cpu_addr (tg68_adr[6:1]),
|
||||
.cpu_uds (tg68_uds ),
|
||||
.cpu_lds (tg68_lds ),
|
||||
.cpu_rw (tg68_rw ),
|
||||
.cpu_dout (vreg_data_out),
|
||||
|
||||
.vaddr (video_address ),
|
||||
.data (ram_data_out_64),
|
||||
.read (video_read ),
|
||||
|
||||
.hs (shifter_hs ),
|
||||
.vs (shifter_vs ),
|
||||
.video_r (shifter_r ),
|
||||
.video_g (shifter_g ),
|
||||
.video_b (shifter_b ),
|
||||
.hs (VGA_HS ),
|
||||
.vs (VGA_VS ),
|
||||
.video_r (VGA_R ),
|
||||
.video_g (VGA_G ),
|
||||
.video_b (VGA_B ),
|
||||
|
||||
// configuration signals
|
||||
.adjust (video_adj ),
|
||||
.pal56 (~system_ctrl[9]),
|
||||
.scanlines (system_ctrl[21:20]),
|
||||
.ste (ste ),
|
||||
.viking_enable ( viking_active ), // enable and activate viking video card
|
||||
.viking_himem ( steroids ), // let viking use memory from $e80000
|
||||
.scandoubler_disable ( scandoubler_disable ), // don't use scandoubler in 15khz modes
|
||||
.adjust ( video_adj ),
|
||||
.pal56 ( ~system_ctrl[9] ),
|
||||
.scanlines ( system_ctrl[21:20] ),
|
||||
.ste ( ste ),
|
||||
|
||||
// signals not affected by scan doubler required for
|
||||
// irq generation
|
||||
@@ -1110,25 +1078,25 @@ wire second_cpu_slot = (mste && enable_16mhz) || steroids;
|
||||
|
||||
// Two of the four cycles are being used. One for video (+STE audio) and one for
|
||||
// cpu, DMA and Blitter. A third is optionally being used for faster CPU
|
||||
wire video_cycle = (bus_cycle == 0);
|
||||
wire video_cycle = (bus_cycle == 0)||(bus_cycle == 2);
|
||||
wire cpu_cycle = (bus_cycle == 1) || (second_cpu_slot && (bus_cycle == 3));
|
||||
wire viking_cycle = (bus_cycle == 2);
|
||||
|
||||
// ----------------- RAM address --------------
|
||||
wire [22:0] video_cycle_addr = (st_hs && ste)?ste_dma_snd_addr:video_address;
|
||||
wire ste_dma_has_bus = (bus_cycle == 0) && st_hs && ste;
|
||||
wire [22:0] video_cycle_addr = ste_dma_has_bus?ste_dma_snd_addr:video_address;
|
||||
wire [22:0] cpu_cycle_addr = dma_has_bus?dma_addr:(blitter_has_bus?blitter_master_addr:tg68_adr[23:1]);
|
||||
wire [22:0] ram_address = viking_cycle?viking_address:(video_cycle?video_cycle_addr:cpu_cycle_addr);
|
||||
wire [22:0] ram_address = video_cycle?video_cycle_addr:cpu_cycle_addr;
|
||||
|
||||
// ----------------- RAM read -----------------
|
||||
// memory access during the video cycle is shared between video and ste_dma_snd
|
||||
wire video_cycle_oe = (st_hs && ste)?ste_dma_snd_read:video_read;
|
||||
wire video_cycle_oe = ste_dma_has_bus?ste_dma_snd_read:video_read;
|
||||
// memory access during the cpu cycle is shared between blitter and cpu
|
||||
wire cpu_cycle_oe = dma_has_bus?dma_read:(blitter_has_bus?blitter_master_read:(cpu_cycle && tg68_as && tg68_rw && cpu2mem));
|
||||
wire ram_oe = viking_cycle?viking_read:(video_cycle?video_cycle_oe:(cpu_cycle?cpu_cycle_oe:1'b0));
|
||||
wire ram_oe = video_cycle?video_cycle_oe:(cpu_cycle?cpu_cycle_oe:1'b0);
|
||||
|
||||
// ----------------- RAM write -----------------
|
||||
wire cpu_cycle_wr = dma_has_bus?dma_write:(blitter_has_bus?blitter_master_write:(cpu_cycle && tg68_as && ~tg68_rw && cpu2ram));
|
||||
wire ram_wr = (viking_cycle||video_cycle)?1'b0:(cpu_cycle?cpu_cycle_wr:1'b0);
|
||||
wire ram_wr = video_cycle?1'b0:(cpu_cycle?cpu_cycle_wr:1'b0);
|
||||
|
||||
wire [15:0] ram_data_out;
|
||||
wire [15:0] system_data_out = cpu2mem?ram_data_out:io_data_out;
|
||||
@@ -1227,6 +1195,8 @@ wire eth_tx_read_strobe, eth_tx_read_begin;
|
||||
wire [7:0] eth_rx_write_byte;
|
||||
wire eth_rx_write_strobe, eth_rx_write_begin;
|
||||
|
||||
wire scandoubler_disable;
|
||||
|
||||
//// user io has an extra spi channel outside minimig core ////
|
||||
user_io user_io(
|
||||
// the spi interface
|
||||
@@ -1274,6 +1244,9 @@ user_io user_io(
|
||||
.eth_rx_write_begin (eth_rx_write_begin),
|
||||
.eth_rx_write_strobe (eth_rx_write_strobe),
|
||||
.eth_rx_write_byte (eth_rx_write_byte),
|
||||
|
||||
// io controller requests to disable vga scandoubler
|
||||
.scandoubler_disable (scandoubler_disable),
|
||||
|
||||
.CORE_TYPE (8'ha3) // mist core id
|
||||
);
|
||||
|
||||
133
cores/mist/osd.v
133
cores/mist/osd.v
@@ -4,7 +4,7 @@
|
||||
// On Screen Display implementation for the MiST board
|
||||
// http://code.google.com/p/mist-board/
|
||||
//
|
||||
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
|
||||
// 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
|
||||
@@ -21,32 +21,74 @@
|
||||
//
|
||||
|
||||
module osd (
|
||||
input clk, // 31.875 MHz
|
||||
input clk, // 31.875 MHz
|
||||
|
||||
// SPI interface for OSD
|
||||
input sck,
|
||||
input ss,
|
||||
input sdi,
|
||||
input sck,
|
||||
input ss,
|
||||
input sdi,
|
||||
|
||||
output reg osd_enable,
|
||||
|
||||
// current video beam position
|
||||
input [9:0] hcnt,
|
||||
input [9:0] vcnt,
|
||||
input hs,
|
||||
input vs,
|
||||
|
||||
input [5:0] in_r, // Red[5:0]
|
||||
input [5:0] in_g, // Green[5:0]
|
||||
input [5:0] in_b, // Blue[5:0]
|
||||
input [5:0] r_in, // Red[5:0]
|
||||
input [5:0] g_in, // Green[5:0]
|
||||
input [5:0] b_in, // Blue[5:0]
|
||||
|
||||
output [5:0] out_r, // Red[5:0]
|
||||
output [5:0] out_g, // Green[5:0]
|
||||
output [5:0] out_b // Blue[5:0]
|
||||
output [5:0] r_out, // Red[5:0]
|
||||
output [5:0] g_out, // Green[5:0]
|
||||
output [5:0] b_out // Blue[5:0]
|
||||
);
|
||||
|
||||
assign out_r = !oe?in_r:{osd_pixel, osd_pixel, osd_pixel, in_r[5:3]};
|
||||
assign out_g = !oe?in_g:{osd_pixel, osd_pixel, 1'b1, in_g[5:3]};
|
||||
assign out_b = !oe?in_b:{osd_pixel, osd_pixel, osd_pixel, in_b[5:3]};
|
||||
// combine input and OSD into a overlayed signal
|
||||
assign r_out = !oe?r_in:{pixel, pixel, pixel, r_in[5:3]};
|
||||
assign g_out = !oe?g_in:{pixel, pixel, 1'b1, g_in[5:3]};
|
||||
assign b_out = !oe?b_in:{pixel, pixel, pixel, b_in[5:3]};
|
||||
|
||||
reg enabled;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------- video timing analysis ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// System clock is 32Mhz. Slowest hsync is 15Khz/64us. Hcounter must thus be
|
||||
// able to run to 2048
|
||||
reg [10:0] hcnt;
|
||||
reg [10:0] vcnt;
|
||||
|
||||
reg [10:0] hs_high;
|
||||
reg [10:0] hs_low;
|
||||
wire hs_pol = hs_high < hs_low;
|
||||
wire [10:0] dsp_width = hs_pol?hs_low:hs_high;
|
||||
|
||||
reg [10:0] vs_high;
|
||||
reg [10:0] vs_low;
|
||||
wire vs_pol = vs_high < vs_low;
|
||||
wire [10:0] dsp_height = vs_pol?vs_low:vs_high;
|
||||
|
||||
reg hsD, vsD;
|
||||
always @(negedge clk) begin
|
||||
// check if hsync has changed
|
||||
hsD <= hs;
|
||||
if(hsD != hs) begin
|
||||
if(hs) hs_low <= hcnt;
|
||||
else hs_high <= hcnt;
|
||||
hcnt <= 11'd0;
|
||||
|
||||
if(hs == hs_pol) begin
|
||||
// check if vsync has changed
|
||||
vsD <= vs;
|
||||
if(vsD != vs) begin
|
||||
if(vs) vs_low <= vcnt;
|
||||
else vs_high <= vcnt;
|
||||
vcnt <= 11'd0;
|
||||
end else
|
||||
vcnt <= vcnt + 11'd1;
|
||||
end
|
||||
end else
|
||||
hcnt <= hcnt + 11'd1;
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// -------------------------------- spi client -------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -59,7 +101,7 @@ reg [7:0] cmd;
|
||||
reg [4:0] cnt;
|
||||
reg [10:0] bcnt;
|
||||
|
||||
reg [7:0] osd_buffer [2047:0]; // the OSD buffer itself
|
||||
reg [7:0] buffer [2047:0]; // the OSD buffer itself
|
||||
|
||||
// the OSD has its own SPI interface to the io controller
|
||||
always@(posedge sck, posedge ss) begin
|
||||
@@ -83,44 +125,51 @@ always@(posedge sck, posedge ss) begin
|
||||
|
||||
// command 0x40: OSDCMDENABLE, OSDCMDDISABLE
|
||||
if(sbuf[6:3] == 4'b0100)
|
||||
osd_enable <= sdi;
|
||||
enabled <= sdi;
|
||||
end
|
||||
|
||||
// command 0x20: OSDCMDWRITE
|
||||
if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin
|
||||
osd_buffer[bcnt] <= {sbuf[6:0], sdi};
|
||||
buffer[bcnt] <= {sbuf[6:0], sdi};
|
||||
bcnt <= bcnt + 11'd1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
localparam OSD_WIDTH = 10'd256;
|
||||
localparam OSD_HEIGHT = 10'd128; // pixels are doubled vertically
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------- OSD position ------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
localparam OSD_POS_X = (10'd640 - OSD_WIDTH)>>1;
|
||||
localparam OSD_POS_Y = (10'd400 - OSD_HEIGHT)>>1;
|
||||
wire expand_x = ((dsp_width > 1000)&&(dsp_height < 1000))?1:0;
|
||||
wire expand_y = (dsp_height > 400)?1:0;
|
||||
|
||||
wire [10:0] width = expand_x?10'd512:10'd256;
|
||||
wire [10:0] height = expand_y?10'd128:10'd64;
|
||||
|
||||
localparam OSD_BORDER = 10'd2;
|
||||
wire [10:0] border_x = expand_x?10'd4:10'd4;
|
||||
wire [10:0] border_y = expand_y?10'd4:10'd2;
|
||||
|
||||
wire oe = osd_enable && (
|
||||
(hcnt >= OSD_POS_X-OSD_BORDER) &&
|
||||
(hcnt < (OSD_POS_X + OSD_WIDTH + OSD_BORDER)) &&
|
||||
(vcnt >= OSD_POS_Y - OSD_BORDER) &&
|
||||
(vcnt < (OSD_POS_Y + OSD_HEIGHT + OSD_BORDER)));
|
||||
wire [10:0] pos_x = (dsp_width - width)>>1;
|
||||
wire [10:0] pos_y = (dsp_height - height)>>1;
|
||||
|
||||
wire osd_content_area =
|
||||
(hcnt >= OSD_POS_X) &&
|
||||
(hcnt < (OSD_POS_X + OSD_WIDTH)) &&
|
||||
(vcnt >= OSD_POS_Y) &&
|
||||
(vcnt < (OSD_POS_Y + OSD_HEIGHT));
|
||||
wire oe = enabled && (
|
||||
(hcnt >= pos_x - border_x) &&
|
||||
(hcnt < (pos_x + width + border_x)) &&
|
||||
(vcnt >= pos_y - border_y) &&
|
||||
(vcnt < (pos_y + height + border_y)));
|
||||
|
||||
wire [7:0] osd_hcnt = hcnt - OSD_POS_X + 7'd1; // one pixel offset for osd_byte register
|
||||
wire [6:0] osd_vcnt = vcnt - OSD_POS_Y;
|
||||
wire content_area =
|
||||
(hcnt >= pos_x) && (hcnt < (pos_x + width - 1)) &&
|
||||
(vcnt >= pos_y) && (vcnt < (pos_y + height - 1));
|
||||
|
||||
wire osd_pixel = osd_content_area?osd_byte[osd_vcnt[3:1]]:1'b0;
|
||||
// one pixel offset for delay by byte register
|
||||
wire [7:0] ihcnt = (expand_x?((hcnt-pos_x)>>1):(hcnt-pos_x))+8'd1;
|
||||
wire [6:0] ivcnt = expand_y?((vcnt-pos_y)>>1):(vcnt-pos_y);
|
||||
|
||||
reg [7:0] osd_byte;
|
||||
wire pixel = content_area?buffer_byte[ivcnt[2:0]]:1'b0;
|
||||
|
||||
reg [7:0] buffer_byte;
|
||||
always @(posedge clk)
|
||||
osd_byte <= osd_buffer[{osd_vcnt[6:4], osd_hcnt}];
|
||||
buffer_byte <= buffer[{ivcnt[5:3], ihcnt}];
|
||||
|
||||
endmodule
|
||||
|
||||
167
cores/mist/scandoubler.v
Normal file
167
cores/mist/scandoubler.v
Normal file
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// scandoubler.v
|
||||
//
|
||||
// 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/>.
|
||||
|
||||
// TODO: Delay vsync one line
|
||||
|
||||
module scandoubler (
|
||||
// system interface
|
||||
input clk, // 31.875 MHz
|
||||
input clk_16, // from shifter
|
||||
|
||||
// scanlines (00-none 01-25% 10-50% 11-75%)
|
||||
input [1:0] scanlines,
|
||||
|
||||
// shifter video interface
|
||||
input hs_in,
|
||||
input vs_in,
|
||||
input [3:0] r_in,
|
||||
input [3:0] g_in,
|
||||
input [3:0] b_in,
|
||||
|
||||
// output interface
|
||||
output reg hs_out,
|
||||
output reg vs_out,
|
||||
output reg [3:0] r_out,
|
||||
output reg [3:0] g_out,
|
||||
output reg [3:0] b_out,
|
||||
|
||||
output is15k
|
||||
);
|
||||
|
||||
// --------------------- create output signals -----------------
|
||||
// latch everything once more to make it glitch free and apply scanline effect
|
||||
reg scanline;
|
||||
always @(posedge clk) begin
|
||||
hs_out <= hs_sd;
|
||||
vs_out <= vs_in;
|
||||
|
||||
// reset scanlines at every new screen
|
||||
if(vs_out != vs_in)
|
||||
scanline <= 1'b0;
|
||||
|
||||
// toggle scanlines at begin of every hsync
|
||||
if(hs_out && !hs_sd)
|
||||
scanline <= !scanline;
|
||||
|
||||
// if no scanlines or not a scanline
|
||||
if(!scanline || scanlines == 2'b00) begin
|
||||
r_out <= sd_out[11:8];
|
||||
g_out <= sd_out[7:4];
|
||||
b_out <= sd_out[3:0];
|
||||
end else begin
|
||||
case(scanlines)
|
||||
2'b01: begin // reduce 25% = 1/2 + 1/4
|
||||
r_out <= { 1'b0, sd_out[11:9] } + { 2'b00, sd_out[11:10] };
|
||||
g_out <= { 1'b0, sd_out[7:5] } + { 2'b00, sd_out[7:6] };
|
||||
b_out <= { 1'b0, sd_out[3:1] } + { 2'b00, sd_out[3:2] };
|
||||
end
|
||||
|
||||
2'b10: begin // reduce 50% = 1/2
|
||||
r_out <= { 1'b0, sd_out[11:9] };
|
||||
g_out <= { 1'b0, sd_out[7:5] };
|
||||
b_out <= { 1'b0, sd_out[3:1] };
|
||||
end
|
||||
|
||||
2'b11: begin // reduce 75% = 1/4
|
||||
r_out <= { 2'b00, sd_out[11:10] };
|
||||
g_out <= { 2'b00, sd_out[7:6] };
|
||||
b_out <= { 2'b00, sd_out[3:2] };
|
||||
end
|
||||
endcase
|
||||
end // else: !if(!scanline || scanlines == 2'b00)
|
||||
end
|
||||
|
||||
// scan doubler output register
|
||||
reg [11:0] sd_out;
|
||||
|
||||
// ==================================================================
|
||||
// ======================== the line buffers ========================
|
||||
// ==================================================================
|
||||
|
||||
// 2 lines of 1024 pixels 3*4 bit RGB
|
||||
reg [11:0] sd_buffer [2047:0];
|
||||
|
||||
// use alternating sd_buffers when storing/reading data
|
||||
reg vsD;
|
||||
reg line_toggle;
|
||||
always @(negedge clk_16) begin
|
||||
vsD <= vs_in;
|
||||
|
||||
if(vsD != vs_in)
|
||||
line_toggle <= 1'b0;
|
||||
|
||||
// begin of incoming hsync
|
||||
if(hsD && !hs_in)
|
||||
line_toggle <= !line_toggle;
|
||||
end
|
||||
|
||||
always @(negedge clk_16)
|
||||
sd_buffer[{line_toggle, hcnt}] <= { r_in, g_in, b_in };
|
||||
|
||||
// ==================================================================
|
||||
// =================== horizontal timing analysis ===================
|
||||
// ==================================================================
|
||||
|
||||
// signal detection of 15khz if hsync frequency is less than 20KHz
|
||||
assign is15k = hs_max > (16000000/20000);
|
||||
|
||||
// total hsync time (in 16MHz cycles), hs_total reaches 1024
|
||||
reg [9:0] hs_max;
|
||||
reg [9:0] hs_rise;
|
||||
reg [9:0] hcnt;
|
||||
reg hsD;
|
||||
|
||||
always @(negedge clk_16) begin
|
||||
hsD <= hs_in;
|
||||
|
||||
// falling edge of hsync indicates start of line
|
||||
if(hsD && !hs_in) begin
|
||||
hs_max <= hcnt;
|
||||
hcnt <= 10'd0;
|
||||
end else
|
||||
hcnt <= hcnt + 10'd1;
|
||||
|
||||
// save position of rising edge
|
||||
if(!hsD && hs_in)
|
||||
hs_rise <= hcnt;
|
||||
end
|
||||
|
||||
// ==================================================================
|
||||
// ==================== output timing generation ====================
|
||||
// ==================================================================
|
||||
|
||||
reg [9:0] sd_hcnt;
|
||||
reg hs_sd;
|
||||
|
||||
// timing generation runs 32 MHz (twice the input signal analysis speed)
|
||||
always @(posedge clk) begin
|
||||
|
||||
// output counter synchronous to input and at twice the rate
|
||||
sd_hcnt <= sd_hcnt + 10'd1;
|
||||
if(hsD && !hs_in) sd_hcnt <= hs_max;
|
||||
if(sd_hcnt == hs_max) sd_hcnt <= 10'd0;
|
||||
|
||||
// replicate horizontal sync at twice the speed
|
||||
if(sd_hcnt == hs_max) hs_sd <= 1'b0;
|
||||
if(sd_hcnt == hs_rise) hs_sd <= 1'b1;
|
||||
|
||||
// read data from line sd_buffer
|
||||
sd_out <= sd_buffer[{~line_toggle, sd_hcnt}];
|
||||
end
|
||||
|
||||
endmodule
|
||||
704
cores/mist/shifter.v
Normal file
704
cores/mist/shifter.v
Normal file
@@ -0,0 +1,704 @@
|
||||
//
|
||||
// shifter.v
|
||||
//
|
||||
// Atari ST(E) shifter implementation for the MiST board
|
||||
// http://code.google.com/p/mist-board/
|
||||
//
|
||||
// Copyright (c) 2013-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 shifter (
|
||||
// system interface
|
||||
input clk, // 31.875 MHz
|
||||
input [1:0] bus_cycle, // bus-cycle for sync
|
||||
|
||||
// memory interface
|
||||
output reg [22:0] vaddr, // video word address counter
|
||||
output read, // video read cycle
|
||||
input [63:0] data, // video data read
|
||||
|
||||
// cpu register interface
|
||||
input cpu_clk,
|
||||
input cpu_reset,
|
||||
input [15:0] cpu_din,
|
||||
input cpu_sel,
|
||||
input [5:0] cpu_addr,
|
||||
input cpu_uds,
|
||||
input cpu_lds,
|
||||
input cpu_rw,
|
||||
output reg [15:0] cpu_dout,
|
||||
|
||||
// screen interface
|
||||
output reg hs, // H_SYNC
|
||||
output reg vs, // V_SYNC
|
||||
output reg [3:0] video_r, // Red
|
||||
output reg [3:0] video_g, // Green
|
||||
output reg [3:0] video_b, // Blue
|
||||
|
||||
// system config
|
||||
input pal56, // use VGA compatible 56hz for PAL
|
||||
input ste, // enable STE featurss
|
||||
|
||||
output vga_hs_pol, // sync polarity to be used on vga
|
||||
output vga_vs_pol,
|
||||
output clk_16, // 16Mhz clock for scan doubler
|
||||
|
||||
// signals not affected by scan doubler for internal use like irqs
|
||||
output st_de,
|
||||
output reg st_vs,
|
||||
output reg st_hs
|
||||
);
|
||||
|
||||
localparam STATE_SYNC = 2'd0;
|
||||
localparam STATE_BLANK = 2'd1;
|
||||
localparam STATE_BORDER = 2'd2;
|
||||
localparam STATE_DISP = 2'd3;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// --------------------------- internal state counter ------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
reg [1:0] t;
|
||||
always @(posedge clk) begin
|
||||
// 32Mhz counter synchronous to 8 Mhz clock
|
||||
// force counter to pass state 0 exactly after the rising edge of clk_reg (8Mhz)
|
||||
if(((t == 2'd3) && ( cpu_clk == 0)) ||
|
||||
((t == 2'd0) && ( cpu_clk == 1)) ||
|
||||
((t != 2'd3) && (t != 2'd0)))
|
||||
t <= t + 2'd1;
|
||||
end
|
||||
|
||||
// give 16Mhz clock to scan doubler
|
||||
assign clk_16 = t[0];
|
||||
|
||||
// create internal bus_cycle signal which is stable on the positive clock
|
||||
// edge and extends the previous state by half a 32 Mhz clock cycle
|
||||
reg [3:0] bus_cycle_L;
|
||||
always @(negedge clk)
|
||||
bus_cycle_L <= { bus_cycle, t };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------ internal signals ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// st_de is the internal display enable signal as used by the mfp. This is used
|
||||
// by software to generate a line interrupt and to e.g. do 512 color effects.
|
||||
// st_de is active low. Using display enable (de) for this makes sure the cpu has
|
||||
// plenty of time before data for the next line is starting to be fetched
|
||||
assign st_de = ~de;
|
||||
|
||||
always @(posedge clk) begin
|
||||
st_hs <= h_sync;
|
||||
|
||||
// hsync irq is generated after the rightmost border pixel column has been displayed
|
||||
|
||||
// hsync starts at begin of blanking phase
|
||||
// if(hcnt == (t1_h_blank_right))
|
||||
// st_hs <= 1'b1;
|
||||
|
||||
// hsync ends at begin of left border
|
||||
// if(hcnt == (t4_h_border_left - 10'd16))
|
||||
// st_hs <= 1'b0;
|
||||
|
||||
// vsync irq is generated right after the last border line has been displayed
|
||||
|
||||
// v_event is the begin of hsync. The hatari video.h says vbi happens 64 clock cycles
|
||||
// ST hor counter runs at 16Mhz, thus the trigger is 128 events after h_sync
|
||||
if(hcnt == v_event) begin
|
||||
// vsync starts at begin of blanking phase
|
||||
if(vcnt == t7_v_blank_bot + 10'd4) st_vs <= 1'b1;
|
||||
|
||||
// vsync ends at begin of top border
|
||||
if(vcnt == t10_v_border_top + 10'd0) st_vs <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// -------------------------------- video mode -------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
wire [121:0] config_string;
|
||||
|
||||
video_modes video_modes(
|
||||
// signals used to select the appropriate mode
|
||||
.mono (mono),
|
||||
.pal (pal),
|
||||
.pal56 (pal56),
|
||||
|
||||
// resulting string containing timing values
|
||||
.mode_str (config_string)
|
||||
);
|
||||
|
||||
// The video config string contains 12 counter values (tX), six for horizontal
|
||||
// timing and six for vertical timing. Each value has 10 bits, the total string
|
||||
// is thus 120 bits long + space for some additional info like sync polarity
|
||||
|
||||
// display border blank(FP) sync blank(BP) border
|
||||
// |--------------------|xxxxxx|#########|_______|##########|xxxxxx|
|
||||
// t0 t1 t2 t3 t4 t5 horizontal
|
||||
// t6 t7 t8 t9 t10 t11 vertical
|
||||
|
||||
// extract the various timing parameters from the config string
|
||||
|
||||
// horizontal timing values are for 640 pixel and are divided by 2 for 320 pixel low rez
|
||||
assign vga_hs_pol = config_string[121];
|
||||
wire [9:0] t0_h_border_right = low?{1'b0,config_string[120:112]}:config_string[120:111];
|
||||
wire [9:0] t1_h_blank_right = low?{1'b0,config_string[110:102]}:config_string[110:101];
|
||||
wire [9:0] t2_h_sync = low?{1'b0,config_string[100:92]}:config_string[100:91];
|
||||
wire [9:0] t3_h_blank_left = low?{1'b0,config_string[90:82]}:config_string[90:81];
|
||||
wire [9:0] t4_h_border_left = low?{1'b0,config_string[80:72]}:config_string[80:71];
|
||||
wire [9:0] t5_h_end = low?{1'b0,config_string[70:62]}:config_string[70:61];
|
||||
|
||||
assign vga_vs_pol = config_string[60];
|
||||
wire [9:0] t6_v_border_bot = config_string[59:50];
|
||||
wire [9:0] t7_v_blank_bot = config_string[49:40];
|
||||
wire [9:0] t8_v_sync = config_string[39:30];
|
||||
wire [9:0] t9_v_blank_top = config_string[29:20];
|
||||
wire [9:0] t10_v_border_top = config_string[19:10];
|
||||
wire [9:0] t11_v_end = config_string[9:0];
|
||||
|
||||
// default video mode is monochrome
|
||||
parameter DEFAULT_MODE = 3'd2;
|
||||
|
||||
// shiftmode register
|
||||
reg [1:0] shmode;
|
||||
wire mono = (shmode == 2'd2);
|
||||
wire mid = (shmode == 2'd1);
|
||||
wire low = (shmode == 2'd0);
|
||||
|
||||
// derive number of planes from shiftmode
|
||||
wire [2:0] planes = mono?3'd1:(mid?3'd2:3'd4);
|
||||
|
||||
reg [1:0] syncmode;
|
||||
reg [1:0] syncmode_latch;
|
||||
wire pal = (syncmode_latch[1] == 1'b1);
|
||||
|
||||
// data input buffers for up to 4 planes
|
||||
reg [15:0] data_latch[4];
|
||||
|
||||
localparam BASE_ADDR = 23'h8000; // default video base address 0x010000
|
||||
reg [22:0] _v_bas_ad; // video base address register
|
||||
|
||||
// 16 colors with 3*4 bits each (4 bits for STE, ST only uses 3 bits)
|
||||
reg [3:0] palette_r[15:0];
|
||||
reg [3:0] palette_g[15:0];
|
||||
reg [3:0] palette_b[15:0];
|
||||
|
||||
// STE-only registers
|
||||
reg [7:0] line_offset; // number of words to skip at the end of each line
|
||||
reg [3:0] pixel_offset; // number of pixels to skip at begin of line
|
||||
reg ste_overscan_enable; // STE has a special 16 bit overscan
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- CPU register read ---------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
always @(cpu_sel, cpu_rw, cpu_uds, cpu_lds, cpu_addr, _v_bas_ad, shmode, vaddr,
|
||||
syncmode, line_offset, pixel_offset, ste) begin
|
||||
cpu_dout = 16'h0000;
|
||||
|
||||
// read registers
|
||||
if(cpu_sel && cpu_rw) begin
|
||||
|
||||
// video base register (r/w)
|
||||
if(cpu_addr == 6'h00) cpu_dout <= { 8'h00, _v_bas_ad[22:15] };
|
||||
if(cpu_addr == 6'h01) cpu_dout <= { 8'h00, _v_bas_ad[14: 7] };
|
||||
if(ste && cpu_addr == 6'h06) cpu_dout <= { 8'h00, _v_bas_ad[ 6: 0], 1'b0 };
|
||||
|
||||
// video address counter (ro on ST)
|
||||
if(cpu_addr == 6'h02) cpu_dout <= { 8'h00, vaddr[22:15] };
|
||||
if(cpu_addr == 6'h03) cpu_dout <= { 8'h00, vaddr[14:7 ] };
|
||||
if(cpu_addr == 6'h04) cpu_dout <= { 8'h00, vaddr[6:0], 1'b0 };
|
||||
|
||||
// syncmode register
|
||||
if(cpu_addr == 6'h05) cpu_dout <= { 6'h00, syncmode, 8'h00 };
|
||||
|
||||
if(ste) begin
|
||||
if(cpu_addr == 6'h07) cpu_dout <= { 8'h00, line_offset };
|
||||
if(cpu_addr == 6'h32) cpu_dout <= { 12'h000, pixel_offset };
|
||||
end
|
||||
|
||||
// the color palette registers
|
||||
if(cpu_addr >= 6'h20 && cpu_addr < 6'h30 ) begin
|
||||
cpu_dout[3:0] <= palette_b[cpu_addr[3:0]];
|
||||
cpu_dout[7:4] <= palette_g[cpu_addr[3:0]];
|
||||
cpu_dout[11:8] <= palette_r[cpu_addr[3:0]];
|
||||
|
||||
// return only the 3 msb in non-ste mode
|
||||
if(!ste) begin
|
||||
cpu_dout[3] <= 1'b0;
|
||||
cpu_dout[7] <= 1'b0;
|
||||
cpu_dout[11] <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// shift mode register
|
||||
if(cpu_addr == 6'h30) cpu_dout <= { 6'h00, shmode, 8'h00 };
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- CPU register write --------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// STE video address write signal is evaluated inside memory engine
|
||||
wire ste_vaddr_write = ste && cpu_sel && !cpu_rw && !cpu_lds;
|
||||
|
||||
always @(negedge cpu_clk) begin
|
||||
if(cpu_reset) begin
|
||||
_v_bas_ad <= BASE_ADDR;
|
||||
shmode <= DEFAULT_MODE; // default video mode 2 => mono
|
||||
syncmode <= 2'b00; // 60hz
|
||||
|
||||
// disable STE hard scroll features
|
||||
line_offset <= 8'h00;
|
||||
pixel_offset <= 4'h0;
|
||||
ste_overscan_enable <= 1'b0;
|
||||
|
||||
palette_b[ 0] <= 4'b111;
|
||||
|
||||
end else begin
|
||||
// write registers
|
||||
if(cpu_sel && !cpu_rw) begin
|
||||
if(!cpu_lds) begin
|
||||
|
||||
// video base address hi/mid (ST and STE)
|
||||
if(cpu_addr == 6'h00) _v_bas_ad[22:15] <= cpu_din[7:0];
|
||||
if(cpu_addr == 6'h01) _v_bas_ad[14:7] <= cpu_din[7:0];
|
||||
|
||||
// In the STE setting hi or mid clears the low byte for ST compatibility
|
||||
// in ST mode this doesn't harm
|
||||
if(cpu_addr[5:1] == 5'h00) _v_bas_ad[6:0] <= 7'h00;
|
||||
|
||||
// the low byte can only be written in STE mode
|
||||
if(ste && cpu_addr == 6'h06) _v_bas_ad[6:0] <= cpu_din[7:1];
|
||||
end
|
||||
|
||||
// writing to sync mode toggles between 50 and 60 hz modes
|
||||
if(cpu_addr == 6'h05 && !cpu_uds) syncmode <= cpu_din[9:8];
|
||||
|
||||
// writing special STE registers
|
||||
if(ste && !cpu_lds) begin
|
||||
if(cpu_addr == 6'h07) line_offset <= cpu_din[7:0];
|
||||
if(cpu_addr == 6'h32) begin
|
||||
pixel_offset <= cpu_din[3:0];
|
||||
ste_overscan_enable <= 1'b0;
|
||||
end
|
||||
|
||||
// Writing the video address counter happens directly inside the
|
||||
// memory engine further below!!!
|
||||
end
|
||||
|
||||
// byte write of 0 to ff8264 while ff8365 (pixel_offset) != 0 results in extra
|
||||
// ste overscan
|
||||
if(ste && !cpu_uds && cpu_lds) begin
|
||||
if((cpu_addr == 6'h32) && (pixel_offset != 0))
|
||||
ste_overscan_enable <= 1'b1;
|
||||
end
|
||||
|
||||
// the color palette registers, always write bit 3 with zero if not in
|
||||
// ste mode as this is the lsb of ste
|
||||
if(cpu_addr >= 6'h20 && cpu_addr < 6'h30 ) begin
|
||||
if(!cpu_uds) begin
|
||||
if(!ste) palette_r[cpu_addr[3:0]] <= { 1'b0 , cpu_din[10:8] };
|
||||
else palette_r[cpu_addr[3:0]] <= cpu_din[11:8];
|
||||
end
|
||||
|
||||
if(!cpu_lds) begin
|
||||
if(!ste) begin
|
||||
palette_g[cpu_addr[3:0]] <= { 1'b0, cpu_din[6:4] };
|
||||
palette_b[cpu_addr[3:0]] <= { 1'b0, cpu_din[2:0] };
|
||||
end else begin
|
||||
palette_g[cpu_addr[3:0]] <= cpu_din[7:4];
|
||||
palette_b[cpu_addr[3:0]] <= cpu_din[3:0];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// make msb writeable if MiST video modes are enabled
|
||||
if(cpu_addr == 6'h30 && !cpu_uds) shmode <= cpu_din[9:8];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// -------------------------- video signal generator -------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ----------------------- monochrome video signal ---------------------------
|
||||
// mono uses the lsb of blue palette entry 0 to invert video
|
||||
wire [3:0] blue0 = palette_b[0];
|
||||
wire mono_bit = blue0[0]^shift_0[15];
|
||||
wire [3:0] mono_rgb = { mono_bit, mono_bit, mono_bit, mono_bit };
|
||||
|
||||
// ------------------------- colour video signal -----------------------------
|
||||
|
||||
// For ST compatibility reasons the STE has the color bit order 0321. This is
|
||||
// handled here
|
||||
wire [3:0] color_index = border?4'd0:{ shift_3[15], shift_2[15], shift_1[15], shift_0[15] };
|
||||
wire [3:0] color_r_pal = palette_r[color_index];
|
||||
wire [3:0] color_r = { color_r_pal[2:0], color_r_pal[3] };
|
||||
wire [3:0] color_g_pal = palette_g[color_index];
|
||||
wire [3:0] color_g = { color_g_pal[2:0], color_g_pal[3] };
|
||||
wire [3:0] color_b_pal = palette_b[color_index];
|
||||
wire [3:0] color_b = { color_b_pal[2:0], color_b_pal[3] };
|
||||
|
||||
// --------------- de-multiplex color and mono into one vga signal -----------
|
||||
wire [3:0] stvid_r = mono?mono_rgb:color_r;
|
||||
wire [3:0] stvid_g = mono?mono_rgb:color_g;
|
||||
wire [3:0] stvid_b = mono?mono_rgb:color_b;
|
||||
|
||||
// shift registers for up to 4 planes
|
||||
reg [15:0] shift_0, shift_1, shift_2, shift_3;
|
||||
|
||||
// clock divider to generate the mid and low rez pixel clocks
|
||||
wire pclk = low?t[1]:mid?t[0]:clk;
|
||||
|
||||
// use variable dot clock
|
||||
always @(posedge pclk) begin
|
||||
hs <= ~h_sync;
|
||||
vs <= ~v_sync;
|
||||
|
||||
// drive video output
|
||||
video_r <= blank?4'b0000:stvid_r;
|
||||
video_g <= blank?4'b0000:stvid_g;
|
||||
video_b <= blank?4'b0000:stvid_b;
|
||||
|
||||
// shift all planes and reload
|
||||
// shift registers every 16 pixels
|
||||
if((hcnt[3:0] == 4'hf)||(hcnt == t5_h_end)) begin
|
||||
if(!ste || (pixel_offset == 0) || ste_overscan_enable) begin
|
||||
shift_0 <= data_latch[0];
|
||||
shift_1 <= data_latch[1];
|
||||
shift_2 <= data_latch[2];
|
||||
shift_3 <= data_latch[3];
|
||||
end else begin
|
||||
shift_0 <= ste_shifted_0;
|
||||
shift_1 <= ste_shifted_1;
|
||||
shift_2 <= ste_shifted_2;
|
||||
shift_3 <= ste_shifted_3;
|
||||
end
|
||||
end else begin
|
||||
shift_0 <= { shift_0[14:0], 1'b0 };
|
||||
shift_1 <= { shift_1[14:0], 1'b0 };
|
||||
shift_2 <= { shift_2[14:0], 1'b0 };
|
||||
shift_3 <= { shift_3[14:0], 1'b0 };
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ----------------------------- overscan detection --------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Currently only opening the bottom border for overscan is supported. Opening
|
||||
// the top border should also be easy. Opening the side borders is basically
|
||||
// impossible as this requires a 100% perfect CPU and shifter timing.
|
||||
|
||||
reg last_syncmode;
|
||||
reg [3:0] bottom_overscan_cnt;
|
||||
reg [3:0] top_overscan_cnt;
|
||||
|
||||
wire bottom_overscan = (bottom_overscan_cnt != 0);
|
||||
wire top_overscan = (top_overscan_cnt != 0);
|
||||
|
||||
always @(posedge clk) begin
|
||||
if(cpu_reset) begin
|
||||
top_overscan_cnt <= 4'd0;
|
||||
bottom_overscan_cnt <= 4'd0;
|
||||
end else begin
|
||||
last_syncmode <= syncmode[1]; // delay syncmode to detect changes
|
||||
|
||||
// reset counters
|
||||
if((vcnt == 0) && (hcnt == 10'd0)) begin
|
||||
if(bottom_overscan_cnt != 0)
|
||||
bottom_overscan_cnt <= bottom_overscan_cnt - 4'd1;
|
||||
|
||||
if(top_overscan_cnt != 0)
|
||||
top_overscan_cnt <= top_overscan_cnt - 4'd1;
|
||||
end
|
||||
|
||||
// this is the magic used to do "overscan".
|
||||
// the magic actually involves more than writing zero (60hz)
|
||||
// within line 200. But this is sufficient for our detection
|
||||
|
||||
// trigger in line 198/199
|
||||
if((vcnt[8:1] == 8'd97)||(vcnt[8:1] == 8'd98)||(vcnt[8:1] == 8'd99)||
|
||||
(vcnt[8:1] == 8'd100)||(vcnt[8:1] == 8'd101)) begin
|
||||
// syncmode has changed from 1 to 0 (50 to 60 hz)
|
||||
if((syncmode[1] == 1'b0) && (last_syncmode == 1'b1))
|
||||
bottom_overscan_cnt <= 4'd15;
|
||||
end
|
||||
|
||||
// trigger in line 284/285
|
||||
if((vcnt[8:1] == 8'd133)||(vcnt[8:1] == 8'd134)||(vcnt[8:1] == 8'd135)||
|
||||
(vcnt[8:1] == 8'd136)||(vcnt[8:1] == 8'd137)||(vcnt[8:1] == 8'd138)) begin
|
||||
// syncmode has changed from 1 to 0 (50 to 60 hz)
|
||||
if((syncmode[1] == 1'b0) && (last_syncmode == 1'b1))
|
||||
top_overscan_cnt <= 4'd15;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// --------------------------- STE hard scroll shifter -----------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// When STE hard scrolling is being used (pixel_offset != 0) then memory reading starts
|
||||
// 16 pixels earlier and data is being moved through an additional shift register
|
||||
|
||||
// extra 32 bit registers required for STE hard scrolling
|
||||
reg [31:0] ste_shift_0, ste_shift_1, ste_shift_2, ste_shift_3;
|
||||
|
||||
// shifted data
|
||||
wire [15:0] ste_shifted_0, ste_shifted_1, ste_shifted_2, ste_shifted_3;
|
||||
|
||||
// connect STE scroll shifters for each plane
|
||||
ste_shifter ste_shifter_0 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_0),
|
||||
.out (ste_shifted_0)
|
||||
);
|
||||
|
||||
ste_shifter ste_shifter_1 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_1),
|
||||
.out (ste_shifted_1)
|
||||
);
|
||||
|
||||
ste_shifter ste_shifter_2 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_2),
|
||||
.out (ste_shifted_2)
|
||||
);
|
||||
|
||||
ste_shifter ste_shifter_3 (
|
||||
.skew (pixel_offset),
|
||||
.in (ste_shift_3),
|
||||
.out (ste_shifted_3)
|
||||
);
|
||||
|
||||
// move data into STE hard scroll shift registers
|
||||
always @(posedge clk) begin
|
||||
if((bus_cycle_L == 4'd08) && (plane == 2'd0)) begin
|
||||
// shift up 16 pixels and load new data into lower bits of shift registers
|
||||
ste_shift_0 <= { ste_shift_0[15:0], data_latch[0] };
|
||||
ste_shift_1 <= { ste_shift_1[15:0], (planes > 3'd1)?data_latch[1]:16'h0000 };
|
||||
ste_shift_2 <= { ste_shift_2[15:0], (planes > 3'd2)?data_latch[2]:16'h0000 };
|
||||
ste_shift_3 <= { ste_shift_3[15:0], (planes > 3'd2)?data_latch[3]:16'h0000 };
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------------- memory engine -----------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
assign read = (bus_cycle == 0) && de; // display enable can directly be used as a ram read signal
|
||||
|
||||
// current plane to be read from memory
|
||||
reg [1:0] plane;
|
||||
|
||||
// To be able to output the first pixel we need to have one word for every plane already
|
||||
// present in memory. We thus need a display enable signal which is (depending on color depth)
|
||||
// 16, 32 or 64 pixel ahead of display enable
|
||||
reg de, de_v;
|
||||
|
||||
// required pixel offset allowing for prefetch of 16 pixels
|
||||
wire [9:0] ste_overscan = ste_overscan_enable?10'd16:10'd0;
|
||||
// ste is starting another 16 pixels earlier if horizontal hard scroll is being used
|
||||
wire [9:0] ste_prefetch = (ste && ((pixel_offset != 0) && !ste_overscan_enable))?10'd16:10'd0;
|
||||
wire [9:0] de_h_start = t5_h_end - 10'd16 - ste_prefetch;
|
||||
wire [9:0] de_h_end = t0_h_border_right - 10'd16 + ste_overscan;
|
||||
|
||||
// extra lines required by vertical overscan
|
||||
wire [9:0] de_v_top_extra = top_overscan?10'd29:10'd0 /* synthesis keep */; // 29 extra ST lines at top
|
||||
wire [9:0] de_v_bot_extra = bottom_overscan?10'd38:10'd0 /* synthesis keep */; // 38 extra ST lines at bottom
|
||||
|
||||
// calculate lines in which active display starts end ends
|
||||
wire [9:0] de_v_start = t11_v_end - de_v_top_extra;
|
||||
wire [9:0] de_v_end = t6_v_border_bot + de_v_bot_extra;
|
||||
|
||||
always @(posedge clk) begin
|
||||
|
||||
// line in which memory access is enabled
|
||||
if(hcnt == v_event) begin
|
||||
if(vcnt == de_v_start) de_v <= 1'b1;
|
||||
if(vcnt == de_v_end) de_v <= 1'b0;
|
||||
end
|
||||
|
||||
// display enable signal 16/32/64 bits (16*planes) ahead of display enable (de)
|
||||
// include bus cycle to stay in sync in scna doubler mode
|
||||
if(de_v) begin
|
||||
if(hcnt == de_h_start) de <= 1'b1;
|
||||
if(hcnt == de_h_end) de <= 1'b0;
|
||||
end
|
||||
|
||||
// make sure each line starts with plane 0
|
||||
if(hcnt == de_h_start)
|
||||
plane <= 2'd0;
|
||||
|
||||
// according to hatari the video counter is reloaded 3 lines before
|
||||
// the vbi occurs. This is right after the display has been painted.
|
||||
// The video address counter is reloaded right after display ends
|
||||
if((hcnt == t3_h_blank_left) && (vcnt == (t7_v_blank_bot+10'd1))) begin
|
||||
vaddr <= _v_bas_ad;
|
||||
|
||||
// copy syncmode
|
||||
syncmode_latch <= syncmode;
|
||||
end else begin
|
||||
|
||||
// video transfer happens in cycle 3 (end of video cycle)
|
||||
if(bus_cycle_L == 3) begin
|
||||
|
||||
// read if display enable is active
|
||||
if(de) begin
|
||||
|
||||
// move incoming video data into data latch
|
||||
// ST shifter only uses 16 out of possible 64 bits, so select the right word
|
||||
case(vaddr[1:0])
|
||||
2'd0: data_latch[plane] <= data[15: 0];
|
||||
2'd1: data_latch[plane] <= data[31:16];
|
||||
2'd2: data_latch[plane] <= data[47:32];
|
||||
2'd3: data_latch[plane] <= data[63:48];
|
||||
endcase
|
||||
|
||||
vaddr <= vaddr + 23'd1;
|
||||
end
|
||||
|
||||
// advance plane counter
|
||||
if(planes != 1) begin
|
||||
plane <= plane + 2'd1;
|
||||
if(plane == planes - 2'd1)
|
||||
plane <= 2'd0;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// STE has additional ways to influence video address
|
||||
if(ste) begin
|
||||
// add line offset at the end of each video line
|
||||
if(de_v && (hcnt == de_h_end) && (t == 0))
|
||||
vaddr <= vaddr + line_offset;
|
||||
|
||||
// STE vaddr write handling
|
||||
// bus_cycle 6 is in the middle of a cpu cycle
|
||||
if((bus_cycle_L == 6) && ste_vaddr_write) begin
|
||||
if(cpu_addr == 6'h02) vaddr[22:15] <= cpu_din[7:0];
|
||||
if(cpu_addr == 6'h03) vaddr[14: 7] <= cpu_din[7:0];
|
||||
if(cpu_addr == 6'h04) vaddr[ 6: 0] <= cpu_din[7:1];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------------- video timing generator --------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
reg [9:0] hcnt; // horizontal pixel counter
|
||||
reg [1:0] h_state; // 0=sync, 1=blank, 2=border, 3=display
|
||||
|
||||
// A seperate vertical timing is not needed, vcnt[9:1] is the st line
|
||||
reg [9:0] vcnt; // vertical line counter
|
||||
reg [1:0] v_state; // 0=sync, 1=blank, 2=border, 3=display
|
||||
|
||||
// blank level is also used during sync
|
||||
wire blank = (v_state == STATE_BLANK) || (h_state == STATE_BLANK) ||
|
||||
(v_state == STATE_SYNC) || (h_state == STATE_SYNC);
|
||||
|
||||
// only the color modes use the border
|
||||
wire border = (v_state == STATE_BORDER)||(h_state == STATE_BORDER);
|
||||
|
||||
// time in horizontal timing where vertical states change (at the begin of the sync phase)
|
||||
wire [9:0] v_event = t2_h_sync;
|
||||
|
||||
reg v_sync, h_sync;
|
||||
|
||||
always @(posedge pclk) begin
|
||||
// ------------- horizontal ST timing generation -------------
|
||||
// Run st timing at full speed if no scan doubler is being used. Otherwise run
|
||||
// it at half speed
|
||||
if(hcnt == t5_h_end) begin
|
||||
// sync hcnt to bus
|
||||
if((low && (bus_cycle_L[3:2] == 2'b11)) ||
|
||||
(mid && (bus_cycle_L[3:1] == 3'b111)) ||
|
||||
(mono && (bus_cycle_L[3:0] == 4'b1111)))
|
||||
hcnt <= 10'd0;
|
||||
end else
|
||||
hcnt <= hcnt + 10'd1;
|
||||
|
||||
if( hcnt == t2_h_sync) h_sync <= 1'b1;
|
||||
if( hcnt == t3_h_blank_left) h_sync <= 1'b0;
|
||||
|
||||
// generate horizontal video signal states
|
||||
if( hcnt == t2_h_sync ) h_state <= STATE_SYNC;
|
||||
if((hcnt == t0_h_border_right + ste_overscan) ||
|
||||
(hcnt == t4_h_border_left)) h_state <= STATE_BORDER;
|
||||
if((hcnt == t1_h_blank_right) || (hcnt == t3_h_blank_left)) h_state <= STATE_BLANK;
|
||||
if( hcnt == t5_h_end) h_state <= STATE_DISP;
|
||||
|
||||
// vertical state changes at begin of hsync
|
||||
if(hcnt == v_event) begin
|
||||
|
||||
// ------------- vertical timing generation -------------
|
||||
// increase vcnt
|
||||
if(vcnt == t11_v_end) vcnt <= 10'd0;
|
||||
else vcnt <= vcnt + 10'd1;
|
||||
|
||||
if( vcnt == t8_v_sync) v_sync <= 1'b1;
|
||||
if( vcnt == t9_v_blank_top) v_sync <= 1'b0;
|
||||
|
||||
// generate vertical video signal states
|
||||
if( vcnt == t8_v_sync ) v_state <= STATE_SYNC;
|
||||
if((vcnt == de_v_end) || (vcnt == t10_v_border_top)) v_state <= STATE_BORDER;
|
||||
if((vcnt == t7_v_blank_bot) || (vcnt == t9_v_blank_top)) v_state <= STATE_BLANK;
|
||||
if( vcnt == de_v_start) v_state <= STATE_DISP;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// --------------------------- STE hard scroll shifter -----------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
module ste_shifter (
|
||||
input [3:0] skew,
|
||||
input [31:0] in,
|
||||
output reg [15:0] out
|
||||
);
|
||||
|
||||
always @(skew, in) begin
|
||||
out = 16'h0000;
|
||||
|
||||
case(skew)
|
||||
15: out = in[16:1];
|
||||
14: out = in[17:2];
|
||||
13: out = in[18:3];
|
||||
12: out = in[19:4];
|
||||
11: out = in[20:5];
|
||||
10: out = in[21:6];
|
||||
9: out = in[22:7];
|
||||
8: out = in[23:8];
|
||||
7: out = in[24:9];
|
||||
6: out = in[25:10];
|
||||
5: out = in[26:11];
|
||||
4: out = in[27:12];
|
||||
3: out = in[28:13];
|
||||
2: out = in[29:14];
|
||||
1: out = in[30:15];
|
||||
0: out = in[31:16];
|
||||
endcase; // case (skew)
|
||||
end
|
||||
|
||||
endmodule
|
||||
136
cores/mist/sync_adjust.v
Normal file
136
cores/mist/sync_adjust.v
Normal file
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// sync_adjust.v
|
||||
//
|
||||
// Ajust the video sync position to allow the user to center the
|
||||
// video on screen
|
||||
//
|
||||
// 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 sync_adjust (
|
||||
// system interface
|
||||
input clk, // 31.875 MHz
|
||||
|
||||
input [15:0] adjust,
|
||||
|
||||
input hs_in,
|
||||
input vs_in,
|
||||
|
||||
output reg hs_out,
|
||||
output reg vs_out
|
||||
);
|
||||
|
||||
// This has to cope with 15kHz (64us). At 32MHz a counter will count
|
||||
// from 0 to 2047 in that time. Thus a 11 bit counter is required
|
||||
|
||||
// Extract and sign extend adjust values
|
||||
wire signed [10:0] adjust_x = { {3{ adjust[15] }}, adjust[15:8] };
|
||||
wire signed [9:0] adjust_y = { {2{ adjust[7] }}, adjust[7:0] };
|
||||
|
||||
// ==================================================================
|
||||
// ====================== input timing analysis =====================
|
||||
// ==================================================================
|
||||
|
||||
// total hsync time (in 32MHz cycles), hs_total reaches 2048
|
||||
reg [10:0] hcnt;
|
||||
reg hsD, vsD;
|
||||
|
||||
// hsync rise at hcnt == 0, signals relative to this:
|
||||
reg [10:0] hs_rise;
|
||||
reg [10:0] hs_fall;
|
||||
reg [10:0] v_event;
|
||||
|
||||
// an event ecactly half a line delayed to v_event to generate
|
||||
// vcntD which is stable over any change in v_event timinig
|
||||
wire [10:0] hs_max_2 = { 1'b0, hs_max[10:1] };
|
||||
wire [10:0] v_event_2 = (v_event > hs_max_2)?v_event-hs_max_2:v_event+hs_max_2;
|
||||
|
||||
reg [9:0] vcnt;
|
||||
reg [9:0] vcntD; // delayed by half a line
|
||||
reg [9:0] vs_rise;
|
||||
reg [9:0] vs_fall;
|
||||
|
||||
// since the counter is restarted at the falling edge, hs_fall contains
|
||||
// the max counter value (total times - 1)
|
||||
wire [10:0] hs_max = hs_fall;
|
||||
wire [9:0] vs_max = vs_fall;
|
||||
|
||||
always @(negedge clk) begin
|
||||
hsD <= hs_in;
|
||||
vsD <= vs_in;
|
||||
|
||||
// hsync has changed
|
||||
hcnt <= hcnt + 11'd1;
|
||||
if(hsD != hs_in) begin
|
||||
if(!hs_in) begin
|
||||
hcnt <= 11'd0;
|
||||
hs_fall <= hcnt;
|
||||
end else
|
||||
hs_rise <= hcnt;
|
||||
end
|
||||
|
||||
if(hcnt == v_event)
|
||||
vcnt <= vcnt + 10'd1;
|
||||
|
||||
// vsync has changed
|
||||
if(vsD != vs_in) begin
|
||||
if(!vs_in) begin
|
||||
v_event <= hcnt;
|
||||
vcnt <= 10'd0;
|
||||
vs_fall <= vcnt;
|
||||
end else
|
||||
vs_rise <= vcnt;
|
||||
end
|
||||
|
||||
if(hcnt == v_event_2)
|
||||
vcntD <= vcnt;
|
||||
end
|
||||
|
||||
// ==================================================================
|
||||
// ==================== output timing generation ====================
|
||||
// ==================================================================
|
||||
|
||||
wire [10:0] hcnt_out_rst = (adjust_x > 0)?(adjust_x-10'd1):(hs_max+adjust_x);
|
||||
reg [10:0] hcnt_out;
|
||||
|
||||
wire [9:0] vcnt_out_rst = (adjust_y > 0)?(adjust_y-9'd1):(vs_max+adjust_y);
|
||||
reg [9:0] vcnt_out;
|
||||
|
||||
always @(posedge clk) begin
|
||||
// generate new hcnt with offset
|
||||
if(hcnt == hcnt_out_rst)
|
||||
hcnt_out <= 11'd0;
|
||||
else
|
||||
hcnt_out <= hcnt_out + 11'd1;
|
||||
|
||||
// generate delayed hsync
|
||||
if(hcnt_out == hs_rise) hs_out <= 1'b1;
|
||||
if(hcnt_out == hs_fall) hs_out <= 1'b0;
|
||||
|
||||
// generate delayed vsync timing
|
||||
if(hcnt_out == v_event) begin
|
||||
|
||||
if(vcntD == vcnt_out_rst)
|
||||
vcnt_out <= 10'd0;
|
||||
else
|
||||
vcnt_out <= vcnt_out + 10'd1;
|
||||
|
||||
// generate delayed vsync
|
||||
if(vcnt_out == vs_rise) vs_out <= 1'b1;
|
||||
if(vcnt_out == vs_fall) vs_out <= 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
@@ -54,7 +54,8 @@ module user_io(
|
||||
|
||||
// on-board buttons and dip switches
|
||||
output [1:0] BUTTONS,
|
||||
output [1:0] SWITCHES
|
||||
output [1:0] SWITCHES,
|
||||
output scandoubler_disable
|
||||
);
|
||||
|
||||
// filter spi clock. the 8 bit gate delay is ~2.5ns in total
|
||||
@@ -65,13 +66,14 @@ reg [1:0] byte_cnt;
|
||||
reg [6:0] sbuf;
|
||||
reg [7:0] cmd;
|
||||
reg [3:0] bit_cnt; // 0..15
|
||||
reg [3:0] but_sw;
|
||||
reg [4:0] but_sw;
|
||||
|
||||
// counter runs 0..7,8..15,8..15,8..15
|
||||
wire [2:0] tx_bit = ~(bit_cnt[2:0]);
|
||||
|
||||
assign BUTTONS = but_sw[1:0];
|
||||
assign SWITCHES = but_sw[3:2];
|
||||
assign scandoubler_disable = but_sw[4];
|
||||
|
||||
always@(negedge spi_sck) begin
|
||||
if(bit_cnt <= 7)
|
||||
@@ -189,10 +191,8 @@ always@(negedge spi_sck) begin
|
||||
if(bit_cnt == 15) begin
|
||||
eth_mac_begin <= 1'b0;
|
||||
|
||||
if(cmd == 1) begin
|
||||
but_sw[3:1] <= sbuf[2:0];
|
||||
but_sw[0] <= SPI_MOSI;
|
||||
end
|
||||
if(cmd == 1)
|
||||
but_sw <= { sbuf[3:0], SPI_MOSI };
|
||||
|
||||
// send ikbd byte to acia
|
||||
if(cmd == 2) begin
|
||||
|
||||
1069
cores/mist/video.v
1069
cores/mist/video.v
File diff suppressed because it is too large
Load Diff
@@ -19,10 +19,13 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// video docs:
|
||||
// VGA video docs:
|
||||
// http://martin.hinner.info/vga/timing.html
|
||||
// http://www.epanorama.net/faq/vga2rgb/calc.html
|
||||
|
||||
// Atari video timings:
|
||||
// http://www.atari-forum.com/viewtopic.php?f=16&t=24855&start=350
|
||||
|
||||
// clocks on real sts:
|
||||
// PAL 32084988 Hz
|
||||
// NTSC 32042400 Hz
|
||||
@@ -39,6 +42,9 @@
|
||||
|
||||
// vbl at cycle counter 64 (64 cycles after hbl)
|
||||
|
||||
// All video modes are based on a 32MHz pixel clock. This is two times the mid rez pixel clock and
|
||||
// four times the low rez pixel clock.
|
||||
|
||||
module video_modes (
|
||||
inout mono, // select monochrome mode (and not color)
|
||||
input pal, // select pal mode (and not ntsc) if a color mode is selected
|
||||
@@ -51,9 +57,6 @@ module video_modes (
|
||||
// ---------------------------- generic timing parameters --------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
localparam H_ACT = 10'd640;
|
||||
localparam V_ACT = 10'd400;
|
||||
|
||||
// TIMING CONSTRAINTS:
|
||||
// The total width (act+both blank+2*border+sync) must be a multiple of 16, for
|
||||
// scan doubled modes a multiple of 8
|
||||
@@ -63,15 +66,16 @@ localparam V_ACT = 10'd400;
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// 56Hz replacement for Atari 50Hz low and medium resolution video mode scan doubled:
|
||||
// displayed 640x200
|
||||
// total: 928x612, active incl border: 720x560, displayed: 640x400
|
||||
// horizontal scan rate: 17.27 kHz ST, 34.48 kHz VGA, vertical scan rate: 56.34 hz
|
||||
|
||||
wire [121:0] pal56_config_str;
|
||||
conf pal56_conf(
|
||||
// front porch sync width back porch border width sync polarity
|
||||
.h_fp ( 10'd44), .h_s (10'd120), .h_bp ( 10'd44), .h_bd (10'd40), .h_sp (1'b1),
|
||||
.v_fp ( 10'd24), .v_s ( 10'd4), .v_bp ( 10'd24), .v_tb (10'd80), .v_bb (10'd80), .v_sp (1'b1),
|
||||
.str (pal56_config_str)
|
||||
// display front porch sync width back porch border width sync polarity
|
||||
.h_ds(10'd640), .h_fp( 10'd44), .h_s(10'd120), .h_bp( 10'd44), .h_lb(10'd40), .h_rb(10'd40), .h_sp(1'b1),
|
||||
.v_ds(10'd200), .v_fp( 10'd12), .v_s( 10'd2), .v_bp( 10'd12), .v_tb(10'd40), .v_bb(10'd40), .v_sp(1'b1),
|
||||
.str (pal56_config_str)
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -79,16 +83,20 @@ conf pal56_conf(
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Atari 50Hz low and medium resolution video mode scan doubled:
|
||||
// According to troed: 40/40/28/320/72/12
|
||||
// -> sync-80 bl-80 brd-56 dsp-640 brd-144 bl-24
|
||||
// displayed 640x200
|
||||
// total: 1024x626, active incl border: 800x560, displayed: 640x400
|
||||
// horizontal scan rate: 15.625 kHz ST, 31.25 kHz VGA, vertical scan rate: 49.92 hz
|
||||
// horizontal scan rate: 15.625 kHz ST (, 31.25 kHz VGA), vertical scan rate: 49.92 hz
|
||||
|
||||
wire [121:0] pal50_config_str;
|
||||
conf pal50_conf(
|
||||
// front porch sync width back porch border width sync polarity
|
||||
.h_fp ( 10'd80), .h_s ( 10'd64), .h_bp ( 10'd80), .h_bd (10'd80), .h_sp (1'b1),
|
||||
// .v_fp ( 10'd42), .v_s ( 10'd8), .v_bp ( 10'd42), .v_tb (10'd58), .v_bb (10'd76), .v_sp (1'b1),
|
||||
.v_fp ( 10'd30), .v_s ( 10'd6), .v_bp ( 10'd30), .v_tb (10'd80), .v_bb (10'd80), .v_sp (1'b1),
|
||||
.str (pal50_config_str)
|
||||
// display front porch sync width back porch border width sync polarity
|
||||
//.h_ds(10'd640), .h_fp( 10'd80), .h_s( 10'd64), .h_bp( 10'd80), .h_lb(10'd80), .h_rb(10'd80), .h_sp(1'b1),
|
||||
.h_ds(10'd640), .h_fp( 10'd80), .h_s( 10'd80), .h_bp( 10'd24), .h_lb(10'd72), .h_rb(10'd128), .h_sp(1'b1),
|
||||
// .h_ds(10'd640), .h_fp( 10'd80), .h_s( 10'd80), .h_bp( 10'd24), .h_lb(10'd56), .h_rb(10'd144), .h_sp(1'b1),
|
||||
.v_ds(10'd200), .v_fp( 10'd15), .v_s( 10'd3), .v_bp( 10'd15), .v_tb(10'd40), .v_bb(10'd40), .v_sp(1'b1),
|
||||
.str (pal50_config_str)
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -101,10 +109,10 @@ conf pal50_conf(
|
||||
|
||||
wire [121:0] ntsc_config_str;
|
||||
conf ntsc_conf(
|
||||
// front porch sync width back porch border width sync polarity
|
||||
.h_fp ( 10'd76), .h_s ( 10'd64), .h_bp ( 10'd76), .h_bd (10'd80), .h_sp (1'b1),
|
||||
.v_fp ( 10'd20), .v_s ( 10'd6), .v_bp ( 10'd20), .v_tb (10'd40), .v_bb (10'd40), .v_sp (1'b0),
|
||||
.str (ntsc_config_str)
|
||||
// display front porch sync width back porch border width sync polarity
|
||||
.h_ds(10'd640), .h_fp( 10'd76), .h_s( 10'd64), .h_bp( 10'd76), .h_lb(10'd80), .h_rb(10'd80), .h_sp(1'b1),
|
||||
.v_ds(10'd200), .v_fp( 10'd10), .v_s( 10'd3), .v_bp( 10'd10), .v_tb(10'd20), .v_bb(10'd20), .v_sp(1'b0),
|
||||
.str (ntsc_config_str)
|
||||
);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -117,10 +125,10 @@ conf ntsc_conf(
|
||||
|
||||
wire [121:0] mono_config_str;
|
||||
conf mono_conf(
|
||||
// front porch sync width back porch border width sync polarity
|
||||
.h_fp (10'd108), .h_s ( 10'd40), .h_bp (10'd108), .h_bd ( 10'd0), .h_sp (1'b0),
|
||||
.v_fp ( 10'd48), .v_s ( 10'd5), .v_bp ( 10'd48), .v_tb ( 10'd0), .v_bb ( 10'd0), .v_sp (1'b0),
|
||||
.str (mono_config_str)
|
||||
// display front porch sync width back porch border width sync polarity
|
||||
.h_ds( 10'd640), .h_fp(10'd108), .h_s( 10'd40), .h_bp(10'd108), .h_lb( 10'd0), .h_rb( 10'd0), .h_sp(1'b0),
|
||||
.v_ds( 10'd400), .v_fp( 10'd48), .v_s( 10'd5), .v_bp( 10'd48), .v_tb( 10'd0), .v_bb( 10'd0), .v_sp(1'b0),
|
||||
.str (mono_config_str)
|
||||
);
|
||||
|
||||
|
||||
@@ -132,44 +140,44 @@ endmodule
|
||||
// ---------------------------------------------------------------------------
|
||||
// ------------------ video timing config string generator -------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
module conf (
|
||||
input [9:0] h_fp, // horizontal front porch width
|
||||
input [9:0] h_s, // horizontal sync width
|
||||
input [9:0] h_bp, // horizontal back porch width
|
||||
input [9:0] h_bd, // horizontal border width
|
||||
input h_sp, // horizontal sync polarity
|
||||
|
||||
input [9:0] v_fp, // vertical front porch width
|
||||
input [9:0] v_s, // vertical sync width
|
||||
input [9:0] v_bp, // vertical back porch width
|
||||
input [9:0] v_tb, // vertical border width top
|
||||
input [9:0] v_bb, // vertical border width bottom
|
||||
input v_sp, // vertical sync polarity
|
||||
module conf (
|
||||
input [9:0] h_ds, // horizontal display
|
||||
input [9:0] h_fp, // horizontal front porch width
|
||||
input [9:0] h_s, // horizontal sync width
|
||||
input [9:0] h_bp, // horizontal back porch width
|
||||
input [9:0] h_lb, // horizontal left border width
|
||||
input [9:0] h_rb, // horizontal right border width
|
||||
input h_sp, // horizontal sync polarity
|
||||
|
||||
input [9:0] v_ds, // vertical display
|
||||
input [9:0] v_fp, // vertical front porch width
|
||||
input [9:0] v_s, // vertical sync width
|
||||
input [9:0] v_bp, // vertical back porch width
|
||||
input [9:0] v_tb, // vertical top border width
|
||||
input [9:0] v_bb, // vertical bottom border width
|
||||
input v_sp, // vertical sync polarity
|
||||
|
||||
output [121:0] str
|
||||
);
|
||||
|
||||
// all Atari video mods are based on a 640x400 screen
|
||||
localparam H_ACT = 10'd640;
|
||||
localparam V_ACT = 10'd400;
|
||||
|
||||
// all parameters are assembled into one config string
|
||||
wire [60:0] h_str = { h_sp,
|
||||
H_ACT - 10'd1,
|
||||
H_ACT + h_bd - 10'd1,
|
||||
H_ACT + h_bd + h_fp - 10'd1,
|
||||
H_ACT + h_bd + h_fp + h_s - 10'd1,
|
||||
H_ACT + h_bd + h_fp + h_s + h_bp - 10'd1,
|
||||
H_ACT + h_bd + h_fp + h_s + h_bp + h_bd - 10'd1};
|
||||
h_ds - 10'd1,
|
||||
h_ds + h_rb - 10'd1,
|
||||
h_ds + h_rb + h_fp - 10'd1,
|
||||
h_ds + h_rb + h_fp + h_s - 10'd1,
|
||||
h_ds + h_rb + h_fp + h_s + h_bp - 10'd1,
|
||||
h_ds + h_rb + h_fp + h_s + h_bp + h_lb - 10'd1};
|
||||
|
||||
wire [60:0] v_str = { v_sp,
|
||||
V_ACT - 10'd1,
|
||||
V_ACT + v_bb - 10'd1,
|
||||
V_ACT + v_bb + v_fp - 10'd1,
|
||||
V_ACT + v_bb + v_fp + v_s - 10'd1,
|
||||
V_ACT + v_bb + v_fp + v_s + v_bp - 10'd1,
|
||||
V_ACT + v_bb + v_fp + v_s + v_bp + v_tb - 10'd1};
|
||||
v_ds - 10'd1,
|
||||
v_ds + v_bb - 10'd1,
|
||||
v_ds + v_bb + v_fp - 10'd1,
|
||||
v_ds + v_bb + v_fp + v_s - 10'd1,
|
||||
v_ds + v_bb + v_fp + v_s + v_bp - 10'd1,
|
||||
v_ds + v_bb + v_fp + v_s + v_bp + v_tb - 10'd1};
|
||||
|
||||
assign str = { h_str, v_str };
|
||||
|
||||
endmodule
|
||||
endmodule
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// Atari ST(E) Viking/SM194
|
||||
// http://code.google.com/p/mist-board/
|
||||
//
|
||||
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
|
||||
// Copyright (c) 2013-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
|
||||
@@ -37,9 +37,9 @@ module viking (
|
||||
// VGA output (multiplexed with sm124 output in top level)
|
||||
output hs,
|
||||
output vs,
|
||||
output [5:0] r,
|
||||
output [5:0] g,
|
||||
output [5:0] b
|
||||
output [3:0] r,
|
||||
output [3:0] g,
|
||||
output [3:0] b
|
||||
);
|
||||
|
||||
localparam BASE = 23'h600000; // c00000
|
||||
@@ -98,8 +98,8 @@ reg[10:0] h_cnt; // 0..2047
|
||||
assign hs = ((h_cnt >= HBP1+H+HFP) && (h_cnt < HBP1+H+HFP+HS))?0:1;
|
||||
always@(posedge pclk) begin
|
||||
if(h_cnt==HBP1+H+HFP+HS+HBP2-1) begin
|
||||
// make sure a line starts with the "viking" bus cyle (2)
|
||||
// shifter has cycle 0, cpu has cycles 1 and 3
|
||||
// make sure a line starts with the "video" bus cyle (0)
|
||||
// cpu has cycles 1 and 3
|
||||
if(bus_cycle_L == { 2'd1, 4'd15 })
|
||||
h_cnt<=0;
|
||||
end else
|
||||
@@ -116,9 +116,6 @@ always@(posedge pclk) begin
|
||||
end
|
||||
end
|
||||
|
||||
// reorder words 1:2:3:4 -> 4:3:2:1
|
||||
wire [63:0] data_reorder = { data[15:0], data[31:16], data[47:32], data[63:48] };
|
||||
|
||||
reg [63:0] input_latch;
|
||||
reg [63:0] shift_register;
|
||||
|
||||
@@ -131,10 +128,13 @@ always@(posedge pclk) begin
|
||||
addr <= addr + 23'd4; // advance 4 words (64 bits)
|
||||
|
||||
if(me && (bus_cycle_L == 6'h2f))
|
||||
input_latch <= data_reorder;
|
||||
input_latch <= data;
|
||||
|
||||
if(bus_cycle_L == 6'h3f)
|
||||
shift_register <= input_latch;
|
||||
// reorder words 1:2:3:4 -> 4:3:2:1
|
||||
shift_register <=
|
||||
{ input_latch[15:0], input_latch[31:16],
|
||||
input_latch[47:32], input_latch[63:48] };
|
||||
else
|
||||
shift_register[63:1] <= shift_register[62:0];
|
||||
end
|
||||
@@ -146,10 +146,10 @@ wire de = (v_cnt < V)&&(h_cnt >= HBP1)&&(h_cnt < HBP1+H);
|
||||
|
||||
wire pix = de?(!shift_register[63]):1'b0;
|
||||
|
||||
// drive all 18 rgb bits from the data bit
|
||||
wire [5:0] pix6 = { pix, pix, pix, pix, pix, pix };
|
||||
assign r = pix6;
|
||||
assign g = pix6;
|
||||
assign b = pix6;
|
||||
// drive all 12 rgb bits from the data bit
|
||||
wire [3:0] pix4 = { pix, pix, pix, pix };
|
||||
assign r = pix4;
|
||||
assign g = pix4;
|
||||
assign b = pix4;
|
||||
|
||||
endmodule
|
||||
|
||||
Reference in New Issue
Block a user