From bdc4d206e8aa507230ac21464775e65628e5fd86 Mon Sep 17 00:00:00 2001 From: harbaum Date: Wed, 18 Feb 2015 20:08:59 +0000 Subject: [PATCH] [ATARI ST] Scandoubler optional, video test bench --- cores/mist/sync_adjust.v | 4 +- cores/mist/video_modes.v | 4 +- tests/verilator/video/Makefile | 26 + tests/verilator/video/high.raw | Bin 0 -> 32000 bytes tests/verilator/video/low.raw | Bin 0 -> 32000 bytes tests/verilator/video/mid.raw | Bin 0 -> 32000 bytes tests/verilator/video/osd.v | 175 +++++++ tests/verilator/video/readme.txt | 31 ++ tests/verilator/video/scandoubler.v | 167 +++++++ tests/verilator/video/shifter.v | 704 ++++++++++++++++++++++++++++ tests/verilator/video/sync_adjust.v | 136 ++++++ tests/verilator/video/video.sav | 52 ++ tests/verilator/video/video.v | 264 +++++++++++ tests/verilator/video/video_modes.v | 183 ++++++++ tests/verilator/video/video_tb.cpp | 606 ++++++++++++++++++++++++ tests/verilator/video/viking.raw | Bin 0 -> 163840 bytes tests/verilator/video/viking.v | 155 ++++++ tests/verilator/video/xvid.raw | Bin 0 -> 393216 bytes 18 files changed, 2503 insertions(+), 4 deletions(-) create mode 100644 tests/verilator/video/Makefile create mode 100644 tests/verilator/video/high.raw create mode 100644 tests/verilator/video/low.raw create mode 100644 tests/verilator/video/mid.raw create mode 100644 tests/verilator/video/osd.v create mode 100644 tests/verilator/video/readme.txt create mode 100644 tests/verilator/video/scandoubler.v create mode 100644 tests/verilator/video/shifter.v create mode 100644 tests/verilator/video/sync_adjust.v create mode 100644 tests/verilator/video/video.sav create mode 100644 tests/verilator/video/video.v create mode 100644 tests/verilator/video/video_modes.v create mode 100644 tests/verilator/video/video_tb.cpp create mode 100644 tests/verilator/video/viking.raw create mode 100644 tests/verilator/video/viking.v create mode 100644 tests/verilator/video/xvid.raw diff --git a/cores/mist/sync_adjust.v b/cores/mist/sync_adjust.v index db6441e..e3195a6 100644 --- a/cores/mist/sync_adjust.v +++ b/cores/mist/sync_adjust.v @@ -102,10 +102,10 @@ end // ==================== output timing generation ==================== // ================================================================== -wire [10:0] hcnt_out_rst = (adjust_x > 0)?(adjust_x-10'd1):(hs_max+adjust_x); +wire [10:0] hcnt_out_rst = (adjust_x < 0)?(10'd0-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); +wire [9:0] vcnt_out_rst = (adjust_y < 0)?(9'd0-adjust_y-9'd1):(vs_max-adjust_y); reg [9:0] vcnt_out; always @(posedge clk) begin diff --git a/cores/mist/video_modes.v b/cores/mist/video_modes.v index 5fdd0c3..caa0b85 100644 --- a/cores/mist/video_modes.v +++ b/cores/mist/video_modes.v @@ -92,8 +92,8 @@ conf pal56_conf( wire [121:0] pal50_config_str; conf pal50_conf( // 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'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) diff --git a/tests/verilator/video/Makefile b/tests/verilator/video/Makefile new file mode 100644 index 0000000..1293a76 --- /dev/null +++ b/tests/verilator/video/Makefile @@ -0,0 +1,26 @@ +PROJECT=video +NOWARN = -Wno-UNOPTFLAT -Wno-WIDTH -Wno-COMBDLY -Wno-CASEINCOMPLETE # --report-unoptflat # -Wno-UNOPTFLAT +SRC = osd.v scandoubler.v shifter.v video_modes.v viking.v sync_adjust.v $(PROJECT).v $(PROJECT)_tb.cpp + +all: $(PROJECT).vcd + +obj_dir/stamp: $(SRC) + verilator $(NOWARN) --cc --trace -CFLAGS `sdl-config --cflags` --exe $(PROJECT).v $(PROJECT)_tb.cpp -LDFLAGS "`sdl-config --libs`" + touch obj_dir/stamp + +obj_dir/V$(PROJECT): obj_dir/stamp + make -j -C obj_dir/ -f V$(PROJECT).mk V$(PROJECT) + +$(PROJECT).vcd: obj_dir/V$(PROJECT) + obj_dir/V$(PROJECT) + +clean: + rm -rf obj_dir + rm -f $(PROJECT).vcd + rm -f *~ + +check: + for i in *.v ; do cmp $$i ../../hdl/mist/$$i ; done + +view: $(PROJECT).vcd + gtkwave $< $(PROJECT).sav & diff --git a/tests/verilator/video/high.raw b/tests/verilator/video/high.raw new file mode 100644 index 0000000000000000000000000000000000000000..140324a047923e2412e899d005d358cc2e46cfb0 GIT binary patch literal 32000 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 EA+Nvx8~^|S literal 0 HcmV?d00001 diff --git a/tests/verilator/video/low.raw b/tests/verilator/video/low.raw new file mode 100644 index 0000000000000000000000000000000000000000..ec18648b9dbbc88281ffc37fa2062401c07e0604 GIT binary patch literal 32000 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 O5j|o#J-dtd|NakES+l7C literal 0 HcmV?d00001 diff --git a/tests/verilator/video/mid.raw b/tests/verilator/video/mid.raw new file mode 100644 index 0000000000000000000000000000000000000000..06246bb9e3c6214a18dfb7e0d8a8ad78906a4d44 GIT binary patch literal 32000 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 literal 0 HcmV?d00001 diff --git a/tests/verilator/video/osd.v b/tests/verilator/video/osd.v new file mode 100644 index 0000000..a2ef648 --- /dev/null +++ b/tests/verilator/video/osd.v @@ -0,0 +1,175 @@ +// +// osd.v +// +// On Screen Display implementation for the MiST board +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2015 Till Harbaum +// +// 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 . +// + +module osd ( + input clk, // 31.875 MHz + + // SPI interface for OSD + input sck, + input ss, + input sdi, + + input hs, + input vs, + + 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] r_out, // Red[5:0] + output [5:0] g_out, // Green[5:0] + output [5:0] b_out // Blue[5:0] +); + +// 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 ------------------------------- +// --------------------------------------------------------------------------- + +// this core supports only the display related OSD commands +// of the minimig + +reg [7:0] sbuf; +reg [7:0] cmd; +reg [4:0] cnt; +reg [10:0] bcnt; + +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 + if(ss == 1'b1) begin + cnt <= 5'd0; + bcnt <= 11'd0; + end else begin + sbuf <= { sbuf[6:0], sdi}; + + // 0:7 is command, rest payload + if(cnt < 15) + cnt <= cnt + 4'd1; + else + cnt <= 4'd8; + + if(cnt == 7) begin + cmd <= {sbuf[6:0], sdi}; + + // lower three command bits are line address + bcnt <= { sbuf[1:0], sdi, 8'h00}; + + // command 0x40: OSDCMDENABLE, OSDCMDDISABLE + if(sbuf[6:3] == 4'b0100) + enabled <= sdi; + end + + // command 0x20: OSDCMDWRITE + if((cmd[7:3] == 5'b00100) && (cnt == 15)) begin + buffer[bcnt] <= {sbuf[6:0], sdi}; + bcnt <= bcnt + 11'd1; + end + end +end + +// --------------------------------------------------------------------------- +// ------------------------------- OSD position ------------------------------ +// --------------------------------------------------------------------------- + +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; + +wire [10:0] border_x = expand_x?10'd4:10'd4; +wire [10:0] border_y = expand_y?10'd4:10'd2; + +wire [10:0] pos_x = (dsp_width - width)>>1; +wire [10:0] pos_y = (dsp_height - height)>>1; + +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 content_area = + (hcnt >= pos_x) && (hcnt < (pos_x + width - 1)) && + (vcnt >= pos_y) && (vcnt < (pos_y + height - 1)); + +// 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); + +wire pixel = content_area?buffer_byte[ivcnt[2:0]]:1'b0; + +reg [7:0] buffer_byte; +always @(posedge clk) + buffer_byte <= buffer[{ivcnt[5:3], ihcnt}]; + +endmodule diff --git a/tests/verilator/video/readme.txt b/tests/verilator/video/readme.txt new file mode 100644 index 0000000..167db27 --- /dev/null +++ b/tests/verilator/video/readme.txt @@ -0,0 +1,31 @@ +video tests +----------- + +These tests run the entire Atari ST video subsystem through a verilator +simulation. The screen is simulated using a SDL window. + +In video_tb.cpp several things can be configured: + +DUMP -- enable signal dump. Slows down simulation +VIKING -- enable simulated viking video card +REZ -- shifter resolution LOW=0, MID=1, HI=2 +SD -- scan doubler on/off +SL -- scanlines 0=off -> 3=75% +PAL -- enable 1-PAL or 0-NTSC +PAL56 -- use 56Hz PAL video modes + +Different modes to be tested: +LOWREZ PAL50 without scan doubler +LOWREZ PAL50 with scan doubler +LOWREZ PAL56 without scan doubler +LOWREZ PAL56 with scan doubler +LOWREZ NTSC without scan doubler +LOWREZ NTSC with scan doubler +MIDREZ PAL50 without scan doubler +MIDREZ PAL50 with scan doubler +MIDREZ PAL56 without scan doubler +MIDREZ PAL56 with scan doubler +MIDREZ NTSC without scan doubler +MIDREZ NTSC with scan doubler +HIREZ +VIKING diff --git a/tests/verilator/video/scandoubler.v b/tests/verilator/video/scandoubler.v new file mode 100644 index 0000000..da97113 --- /dev/null +++ b/tests/verilator/video/scandoubler.v @@ -0,0 +1,167 @@ +// +// scandoubler.v +// +// Copyright (c) 2015 Till Harbaum +// +// 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 . + +// 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 diff --git a/tests/verilator/video/shifter.v b/tests/verilator/video/shifter.v new file mode 100644 index 0000000..ffcbbef --- /dev/null +++ b/tests/verilator/video/shifter.v @@ -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 +// +// 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 . + +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 diff --git a/tests/verilator/video/sync_adjust.v b/tests/verilator/video/sync_adjust.v new file mode 100644 index 0000000..e3195a6 --- /dev/null +++ b/tests/verilator/video/sync_adjust.v @@ -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 +// +// 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 . + +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)?(10'd0-adjust_x-10'd1):(hs_max-adjust_x); +reg [10:0] hcnt_out; + +wire [9:0] vcnt_out_rst = (adjust_y < 0)?(9'd0-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 diff --git a/tests/verilator/video/video.sav b/tests/verilator/video/video.sav new file mode 100644 index 0000000..d28f38a --- /dev/null +++ b/tests/verilator/video/video.sav @@ -0,0 +1,52 @@ +[*] +[*] GTKWave Analyzer v3.3.58 (w)1999-2014 BSI +[*] Wed Feb 18 14:49:30 2015 +[*] +[dumpfile] "/home/tharbaum/tmp/mist/tools/verilator_video/video.vcd" +[dumpfile_mtime] "Wed Feb 18 14:46:53 2015" +[dumpfile_size] 153829409 +[savefile] "/home/tharbaum/tmp/mist/tools/verilator_video/video.sav" +[timestart] 14081095 +[size] 1134 700 +[pos] 19 117 +*-10.703690 14083937 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 +[treeopen] TOP. +[treeopen] TOP.v. +[treeopen] TOP.v.shifter. +[sst_width] 202 +[signals_width] 206 +[sst_expanded] 1 +[sst_vpaned_height] 250 +@28 +TOP.v.clk_32 +@22 +TOP.v.vaddr[22:0] +@28 +TOP.v.shifter.h_state[1:0] +TOP.v.read +@22 +TOP.v.shifter.data_latch(0)[15:0] +TOP.v.shifter.data_latch(1)[15:0] +TOP.v.shifter.data_latch(2)[15:0] +TOP.v.shifter.data_latch(3)[15:0] +TOP.v.shifter.shift_0[15:0] +TOP.v.shifter.shift_1[15:0] +TOP.v.shifter.shift_2[15:0] +TOP.v.shifter.shift_3[15:0] +TOP.v.shifter_r[3:0] +TOP.v.shifter_g[3:0] +TOP.v.shifter_b[3:0] +@28 +TOP.v.shifter.pclk +@22 +TOP.v.shifter.bus_cycle_L[3:0] +@28 +TOP.v.shifter.t[1:0] +@24 +TOP.v.shifter.hcnt[9:0] +@29 +TOP.v.shifter.bus_cycle[1:0] +@28 +TOP.v.shifter.de +[pattern_trace] 1 +[pattern_trace] 0 diff --git a/tests/verilator/video/video.v b/tests/verilator/video/video.v new file mode 100644 index 0000000..551ffba --- /dev/null +++ b/tests/verilator/video/video.v @@ -0,0 +1,264 @@ +// +// video.v +// +// Copyright (c) 2015 Till Harbaum +// +// 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 . + +module video ( + // system interface + input clk_128, // 127.5 MHz + input clk_32, // 31.875 MHz + input [1:0] bus_cycle, // bus-cycle for sync + + // SPI interface for OSD + input sck, + input ss, + input sdi, + + // memory interface + output [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 [15:0] cpu_dout, + + // screen interface + output hs, // H_SYNC + output vs, // V_SYNC + output [5:0] video_r, // Red[5:0] + output [5:0] video_g, // Green[5:0] + output [5:0] video_b, // Blue[5:0] + + // system config + input viking_enable, // enable viking video card + input viking_himem, // let viking use memory from $e80000 + input scandoubler_disable, // don't use scandoubler in 15khz modes + input pal56, // use VGA compatible 56hz for PAL + input [1:0] scanlines, // scanlines (00-none 01-25% 10-50% 11-100%) + input [15:0] adjust, // hor/ver video adjust + input ste, // enable STE featurss + + // signals not affected by scan doubler for internal use like irqs + output st_de, + output st_vs, + output st_hs +); + +// give viking access to the memory if it's enabled +assign vaddr = viking_enable?viking_vaddr:shifter_vaddr; +assign read = viking_enable?viking_read:shifter_read; + +// if we use 15khz signals without scan doubler then we need +// to create a composite sync on hsync +wire enable_csync = sd_15khz_detected && scandoubler_disable; +wire csync = shifter_hs == shifter_vs; +assign hs = enable_csync?csync:stvid_hs; +assign vs = enable_csync?1'b1:stvid_vs; + +// ------------------------- OSD --------------------------- + +// in viking mode OSD is operated at 64 MHz pixel clock +reg clk_64; +always @(posedge clk_128) + clk_64 <= !clk_64; + +wire osd_clk = viking_enable?clk_128:clk_32; + +// include OSD overlay +osd osd ( + .clk ( osd_clk ), + + // OSD spi interface to io controller + .sdi ( sdi ), + .sck ( sck ), + .ss ( ss ), + + // feed ST video signal into OSD + .hs ( stvid_hs ), + .vs ( stvid_vs ), + + .r_in ( {stvid_r, 2'b00}), + .g_in ( {stvid_g, 2'b00}), + .b_in ( {stvid_b, 2'b00}), + + // receive signal with OSD overlayed + .r_out ( video_r ), + .g_out ( video_g ), + .b_out ( video_b ) +); + +// ------------- combine scandoubled shifter with viking ------------- +wire [3:0] stvid_r = viking_enable?viking_r:shifter_sd_r; +wire [3:0] stvid_g = viking_enable?viking_g:shifter_sd_g; +wire [3:0] stvid_b = viking_enable?viking_b:shifter_sd_b; +wire stvid_hs = viking_enable?viking_hs:vga_hs; +wire stvid_vs = viking_enable?viking_vs:vga_vs; + +// --------------- apply screen position adjustments ----------------- + +// apply vga sync polarity adjustment to scan doubler output. It doesn't hurt +// to do this even if 15khz modes are being used since the 15khz modes generate +// their csync signals from other signals +wire vga_hs = shifter_sd_adjusted_hs ^ vga_hs_pol; +wire vga_vs = shifter_sd_adjusted_vs ^ vga_vs_pol; + +wire shifter_sd_adjusted_hs; +wire shifter_sd_adjusted_vs; + +sync_adjust sync_adjust ( + .clk ( clk_32 ), + .adjust ( adjust ), + + .hs_in ( shifter_sd_hs ), + .vs_in ( shifter_sd_vs ), + + .hs_out ( shifter_sd_adjusted_hs ), + .vs_out ( shifter_sd_adjusted_vs ) +); + +// --------------- combine shifter with scan doubler ----------------- + +// use scandoubler if 15khz signal has been detected and +// scandoubler isn't disabled +wire use_scandoubler = sd_15khz_detected && !scandoubler_disable; + +// forward scandoubled signals whenever scandouble is to be used +wire [3:0] shifter_sd_r = use_scandoubler?sd_r:shifter_r; +wire [3:0] shifter_sd_g = use_scandoubler?sd_g:shifter_g; +wire [3:0] shifter_sd_b = use_scandoubler?sd_b:shifter_b; +wire shifter_sd_hs = use_scandoubler?sd_hs:shifter_hs; +wire shifter_sd_vs = use_scandoubler?sd_vs:shifter_vs; + +// --------------- the scan doubler for 15khz modes ----------------- +wire sd_15khz_detected; +wire sd_hs, sd_vs; +wire [3:0] sd_r, sd_g, sd_b; + +scandoubler scandoubler ( + .clk ( clk_32 ), // 31.875 MHz + .clk_16 ( clk_16 ), + + .scanlines ( scanlines ), + + // video input from shifter + .hs_in ( shifter_hs ), + .vs_in ( shifter_vs ), + .r_in ( shifter_r ), + .g_in ( shifter_g ), + .b_in ( shifter_b ), + + // output interface + .hs_out ( sd_hs ), + .vs_out ( sd_vs ), + .r_out ( sd_r ), + .g_out ( sd_g ), + .b_out ( sd_b ), + + .is15k ( sd_15khz_detected ) +); + +// --------------- the Atari ST(E) shifter chip ----------------- +wire shifter_hs, shifter_vs; +wire [3:0] shifter_r, shifter_g, shifter_b; + +wire [22:0] shifter_vaddr; +wire shifter_read; + +// sync polarity to be used when outputting to VGA +wire vga_hs_pol, vga_vs_pol; + +// only use pal56 modes if the scandoubler is being used +wire use_pal56 = pal56 && !scandoubler_disable; + +wire clk_16; + +shifter shifter ( + .clk ( clk_32 ), // 31.875 MHz + .bus_cycle ( bus_cycle ), // to sync memory access with cpu + + // memory interface + .vaddr ( shifter_vaddr ), // video word address + .read ( shifter_read ), // video read cycle + .data ( data ), // video data read + + // cpu register interface + .cpu_clk ( cpu_clk ), + .cpu_reset ( cpu_reset ), + .cpu_din ( cpu_din ), + .cpu_sel ( cpu_sel ), + .cpu_addr ( cpu_addr ), + .cpu_uds ( cpu_uds ), + .cpu_lds ( cpu_lds ), + .cpu_rw ( cpu_rw ), + .cpu_dout ( cpu_dout ), + + // screen interface + .hs ( shifter_hs ), // H_SYNC + .vs ( shifter_vs ), // V_SYNC + .video_r ( shifter_r ), // Red[5:0] + .video_g ( shifter_g ), // Green[5:0] + .video_b ( shifter_b ), // Blue[5:0] + + // sync polarity to be used on vga + .vga_vs_pol ( vga_vs_pol ), + .vga_hs_pol ( vga_hs_pol ), + .clk_16 ( clk_16 ), + + // system config + .pal56 ( use_pal56 ), // use VGA compatible 56hz for PAL + .ste ( ste ), // enable STE features + + // signals not affected by scan doubler for internal use like irqs + .st_de ( st_de ), + .st_vs ( st_vs ), + .st_hs ( st_hs ) +); + +// --------------- the Viking compatible 1280x1024 graphics card ----------------- +wire viking_hs, viking_vs; +wire [3:0] viking_r, viking_g, viking_b; + +wire [22:0] viking_vaddr; +wire viking_read; + +viking viking ( + .pclk ( clk_128 ), // 128MHz + .himem ( viking_himem ), + .bclk ( cpu_clk ), + .bus_cycle ( bus_cycle ), // bus-cycle to sync video memory access with cpu + + // memory interface + .addr ( viking_vaddr ), // video word address + .read ( viking_read ), // video read cycle + .data ( data ), // video data read + + // video output + .hs ( viking_hs ), + .vs ( viking_vs ), + .r ( viking_r ), + .g ( viking_g ), + .b ( viking_b ) +); + +endmodule diff --git a/tests/verilator/video/video_modes.v b/tests/verilator/video/video_modes.v new file mode 100644 index 0000000..caa0b85 --- /dev/null +++ b/tests/verilator/video/video_modes.v @@ -0,0 +1,183 @@ +// +// video_modes.v +// +// Video modes for Atari ST shifter implementation for the MiST board +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2013 Till Harbaum +// +// 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 . + +// 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 +// MIST 31875000 Hz + +// real ST timing + +// Starting with VBI +// Atari Timing as hatari sees it: sync 34, border 29, disp 200, 47 border, 3 ?? = 313, vbi@310 +// 47 bottom border lines seesm to be too much, some intros have artifacts in the lower lines +// 38 bottom border lines seems to be good + +// 60Hz sync 5, border 29, disp 200, 29 border = 263, vbi@261 + +// 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 + input pal56, // use a 56 hz mode if pal mode is selected + + output [121:0] mode_str +); + +// --------------------------------------------------------------------------- +// ---------------------------- generic timing parameters -------------------- +// --------------------------------------------------------------------------- + +// 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 + +// --------------------------------------------------------------------------- +// ----------------------------- pal56 timing ------------------------------- +// --------------------------------------------------------------------------- + +// 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( + // 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) +); + +// --------------------------------------------------------------------------- +// ----------------------------- pal50 timing ------------------------------- +// --------------------------------------------------------------------------- + +// 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 + +wire [121:0] pal50_config_str; +conf pal50_conf( + // 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) +); + +// --------------------------------------------------------------------------- +// ------------------------------ ntsc timing ------------------------------- +// --------------------------------------------------------------------------- + +// Atari 60Hz low and medium resolution video mode scan doubled: +// total: 1016x526, active incl border: 800x480, displayed: 640x400 +// horizontal scan rate: 15.748 kHz ST, 31.5 kHz VGA, vertical scan rate: 59.88 hz + +wire [121:0] ntsc_config_str; +conf ntsc_conf( + // 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) +); + +// --------------------------------------------------------------------------- +// ------------------------------ mono timing ------------------------------- +// --------------------------------------------------------------------------- + +// Atari 71Hz high resolution video mode: +// total: 896x501, displayed: 640x400 +// horizontal scan rate: 35.714 kHz ST/VGA, vertical scan rate: 71.286 hz + +wire [121:0] mono_config_str; +conf mono_conf( + // 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) +); + + +// this is the video mode multiplexer ... +assign mode_str = mono?mono_config_str:(pal?(pal56?pal56_config_str:pal50_config_str):ntsc_config_str); + +endmodule + +// --------------------------------------------------------------------------- +// ------------------ video timing config string generator ------------------- +// --------------------------------------------------------------------------- + +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 parameters are assembled into one config string +wire [60:0] h_str = { h_sp, + 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_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 diff --git a/tests/verilator/video/video_tb.cpp b/tests/verilator/video/video_tb.cpp new file mode 100644 index 0000000..797403d --- /dev/null +++ b/tests/verilator/video/video_tb.cpp @@ -0,0 +1,606 @@ +#include + +#include "Vvideo.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +// analyze video mode and compare with: +// http://alive.atari.org/alive9/ovrscn1.php + +// TODO: +// - ST hsync timing +// - hsync 16 pixel later +// - use shifter sync signals for ST directly +// - NTSC timing from Troed/Overscan +// - v-event position from Troed (4 cycles before DE in ST LOW) +// Synthesis: +// - OSD right border +// - check STE sound + +#define DUMP 1 +#define VIKING 0 // enable viking card +#define REZ 0 // LOW=0, MID=1, HI=2 +#define SD 1 // scan doubler on/off +#define SL 2 // scanlines 0=off -> 3=75% +#define PAL 1 // 0-NTSC or 1-PAL +#define PAL56 0 // enable PAL56 mode + +#define STE_SHIFT 0 // 1 +#define STE_LINE_OFFSET 0 + +#define CLK (31875000.0) + +#if VIKING +#define W 900 +#define H 540 +#else +#if REZ==0 && SD==0 +#define W 513 +#else +#define W 1026 +#endif + +#if REZ==2 || SD==1 +#define H 626 +#else +#define H 313 +#endif +#endif + +unsigned char vidmem[1280*1024/8]; + +SDL_Surface* screen = NULL; + +Vvideo* top = NULL; +#if DUMP +VerilatedVcdC* tfp = NULL; +#endif + +double time_ns = 0; + +#define MHZ2NS(a) (1000000000.0/(a)) + +void hexdump(void *data, int size) { + int i, b2c, n=0; + char *ptr = (char*)data; + + if(!size) return; + + while(size>0) { + printf(" %04x: ", n); + b2c = (size>16)?16:size; + for(i=0;ipixels; + // Set the pixel +#if VIKING + // average half size + if((x < 2*W) && (y < 2*H)) { + pixel = (pixel>>2)&0x3f3f3f; + if(!(y&1) && !(x&1)) + pixels[ ( y/2 * screen->w ) + x/2 ] = pixel; + else + pixels[ ( y/2 * screen->w ) + x/2 ] += pixel; + } +#else + if((x < W) && (y < H)) + pixels[ ( y * screen->w ) + x ] = pixel; +#endif +} + +int dump_enabled = 0; + +void eval(void) { + // evaluate recent changes + top->eval(); + +#if DUMP + if(dump_enabled) + tfp->dump(time_ns); +#endif + + // check if hsync changes + { static int hs = 0; + static double hs_ns = 0; + static double hs_hi_ns = 0; + static double hs_lo_ns = 0; + static double hs_tot_ns = 0; + static unsigned long last_addr = 0; + static int last_addr_inc = 0; + + if(top->v__DOT__stvid_hs != hs) { + if(hs_ns) { + double hs_time = time_ns - hs_ns; + int change = 0; + + if(hs) { + if(fabs(hs_hi_ns - hs_time) > 0.001) change = 1; + hs_hi_ns = hs_time; + } else { + if(fabs(hs_lo_ns - hs_time) > 0.001) change = 1; + hs_lo_ns = hs_time; + } + double hs_tot_ns = hs_lo_ns + hs_hi_ns; + if(change && hs_lo_ns && hs_hi_ns) + printf("HSYNC changed line in %d HI/LO %.3fus/%.3fus, tot %.3fus / %.3fkhz\n", + top->v__DOT__shifter__DOT__vcnt, hs_hi_ns/1000, hs_lo_ns/1000, + hs_tot_ns/1000, 1000000/hs_tot_ns); + } + + hs_ns = time_ns; + hs = top->v__DOT__stvid_hs; + } + } + + // check if vsync changes + { static int vs = 0; + static double vs_ns = 0; + static double vs_hi_ns = 0; + static double vs_lo_ns = 0; + static double vs_tot_ns = 0; + if(top->v__DOT__stvid_vs != vs) { + if(vs_ns) { + double vs_time = time_ns - vs_ns; + int change = 0; + + if(vs) { + if(fabs(vs_hi_ns - vs_time) > 1) change = 1; + vs_hi_ns = vs_time; + } else { + if(fabs(vs_lo_ns - vs_time) > 1) change = 1; + vs_lo_ns = vs_time; + } + double vs_tot_ns = vs_lo_ns + vs_hi_ns; + if(change && vs_lo_ns && vs_hi_ns) + printf("VSYNC HI/LO %.3fms/%.3fms, tot %.3fms / %.3fhz\n", + vs_hi_ns/1000000, vs_lo_ns/1000000, vs_tot_ns/1000000, 1000000000/vs_tot_ns); + } + + vs_ns = time_ns; + vs = top->v__DOT__stvid_vs; + } + } + + // eval on negedge of 8 mhz clk + { static int last_cpu_clk = 0; + if(!top->cpu_clk && last_cpu_clk) { + if(top->read) { + unsigned long long v; +#if VIKING + // viking can address up to 256kb + memcpy(&v, vidmem+2*(top->vaddr&0x1fffc), 8); +#else + memcpy(&v, vidmem+2*(top->vaddr&0x7ffc), 8); +#endif + top->data = + ((v & 0xff00ff00ff00ff00) >> 8) | + ((v & 0x00ff00ff00ff00ff) << 8); + + // Bus cycles 0 and 2 may be used by video + // Usually shifter uses 0 (incl STE DMA audio) + // Viking uses 2 + // And MISTXVID uses 0 and 2 (and thus doesn't support STE DMA audio) + if((top->bus_cycle != 0)&&(top->bus_cycle != 2)) { + printf("illegal read in bus_cycle %d\n", top->bus_cycle); + exit(-1); + } + } + } + last_cpu_clk = top->cpu_clk; + } + + // eval on negedge of 32 mhz clk + if(dump_enabled) { + static int last_clk = 0; + // scan doubled output is always analyzed at 32MHz + if(! +#if VIKING + top->clk_128 +#elif SD + top->clk_32 +#else + top->v__DOT__shifter__DOT__pclk +#endif + && last_clk) { + static int last_hs=0, last_vs=0; + static int x=0, y=0; + + put_pixel32(x, y, + (top->video_r<<18) + (top->video_g<<10) + (top->video_b<<2)); + + // draw hsync in dark red + if(top->v__DOT__stvid_hs == top->v__DOT__osd__DOT__hs_pol) { + put_pixel32(x, y, 0x800000); + + // all pixels should be black during sync, highlight other ones in green + if((top->video_r != 0) || (top->video_g != 0) || (top->video_b != 0)) + put_pixel32(x, y, 0x00ff00); + } + + x++; + if(top->v__DOT__stvid_hs != last_hs) { + // and of hsync + if(last_hs == top->v__DOT__osd__DOT__hs_pol) + { x = 0; y++; } + last_hs = top->v__DOT__stvid_hs; + + /* update the screen */ + SDL_UpdateRect(screen, 0, 0, 0, 0); + } + if(top->v__DOT__stvid_vs != last_vs) { + if(top->v__DOT__stvid_vs) y = 0; + last_vs = top->v__DOT__stvid_vs; + } + } +#if VIKING + last_clk = top->clk_128; +#elif SD + last_clk = top->clk_32; +#else + last_clk = top->v__DOT__shifter__DOT__pclk; +#endif + } +} + +unsigned long cpu_write_addr = 0; +unsigned short cpu_write_data; + +// advance time and create valid 8 Mhz clock and signals +// derived from it +void wait_ns(double n) { + static double clk_time = 0; + + eval(); + + // check if next clk event is within waiting period + while(clk_time <= n) { + time_ns += clk_time; // advance time to next clk event + n -= clk_time; // reduce remainung waiting time + + // process change on clk +#if VIKING + // viking needs 128MHz + top->clk_128 = !top->clk_128; + eval(); + static int x = 0; + if(x++ == 3) { + x = 0; +#else + { +#endif + + top->clk_32 = !top->clk_32; + eval(); + + // things supposed to happen on rising clock edge + if(top->clk_32) { + // every 4th cycle ... + static int clk_cnt = 0; + + if(clk_cnt == 1) + top->bus_cycle = (top->bus_cycle + 1) &3; + + clk_cnt = clk_cnt + 1; + top->cpu_clk = (clk_cnt&2)?1:0; // 8MHz + + if(clk_cnt == 4) clk_cnt = 0; + + // ------------ cpu access --------------- + if(clk_cnt == 2) { + top->cpu_sel = 0; + + if(top->bus_cycle == 0) { + + // perform cpu write access + if(cpu_write_addr) { + printf("CPU WRITE $%lx = $%x\n", cpu_write_addr, cpu_write_data); + + top->cpu_sel = (cpu_write_addr & ~0xff) == 0xff8200; + top->cpu_addr = (cpu_write_addr & 0xff)>>1; + top->cpu_rw = 0; + top->cpu_din = cpu_write_data; + top->cpu_uds = top->cpu_lds = 0; + cpu_write_addr = 0; + } + } + } + } + } + eval(); + +#if VIKING + clk_time = MHZ2NS(4*CLK)/2.0; // next clk change in 3.9ns +#else + clk_time = MHZ2NS(CLK)/2.0; // next clk change in 31.25ns +#endif + } + + // next event is when done waiting + time_ns += n; // advance time + clk_time -= n; +} + +void wait_us(double n) { + wait_ns(n * 1000.0); +} + +void wait_ms(double n) { + wait_us(n * 1000.0); +} + +void cpu_write_short(unsigned long addr, unsigned short data) { + cpu_write_addr = addr; + cpu_write_data = ((data & 0xff)<<8) | ((data & 0xff00)>>8); + wait_us(1); // wait two 2MHz system cycles +} + +int main(int argc, char **argv, char **env) { + +#if STE_SHIFT != 0 +#if REZ == 0 +#define XTRA_OFFSET (8+2*STE_LINE_OFFSET) // 16 pixels * 4 bit +#elif REZ == 1 +#define XTRA_OFFSET (4+2*STE_LINE_OFFSET) // 16 pixels * 2 bit +#else +#define XTRA_OFFSET (2+2*STE_LINE_OFFSET) // 16 pixels * 1 bit +#endif +#else +#define XTRA_OFFSET (0+2*STE_LINE_OFFSET) +#endif + + memset(vidmem, 0x80, sizeof(vidmem)); + + // load image +#if VIKING + FILE *in = fopen("viking.raw", "rb"); + if(in) { + fread(vidmem, 1280*1024/8, 1, in); + fclose(in); + } + +#else +#if REZ == 0 + FILE *in = fopen("low.raw", "rb"); +#elif REZ == 1 + FILE *in = fopen("mid.raw", "rb"); +#else + FILE *in = fopen("high.raw", "rb"); +#endif + if(in) { + // load single lines with offset if wanted + int i; + unsigned char *p = vidmem; + for(i=0;i<200;i++) { + fread(p, 160, 1, in); + p += 160+XTRA_OFFSET; + } + fclose(in); + } +#endif + +#if 0 + // add some test pattern to the begin + { + int x; for(x=0;x<8;x++) { + // top left + vidmem[x*(160+XTRA_OFFSET)+0] = 0x55; vidmem[x*(160+XTRA_OFFSET)+1] = 0x55; + vidmem[x*(160+XTRA_OFFSET)+2] = 0x33; vidmem[x*(160+XTRA_OFFSET)+3] = 0x33; + vidmem[x*(160+XTRA_OFFSET)+4] = 0x0f; vidmem[x*(160+XTRA_OFFSET)+5] = 0x0f; + vidmem[x*(160+XTRA_OFFSET)+6] = 0x00; vidmem[x*(160+XTRA_OFFSET)+7] = 0xff; + + // top right + vidmem[x*(160+XTRA_OFFSET)+152+XTRA_OFFSET] = 0x55; + vidmem[x*(160+XTRA_OFFSET)+153+XTRA_OFFSET] = 0x55; + vidmem[x*(160+XTRA_OFFSET)+154+XTRA_OFFSET] = 0x33; + vidmem[x*(160+XTRA_OFFSET)+155+XTRA_OFFSET] = 0x33; + vidmem[x*(160+XTRA_OFFSET)+156+XTRA_OFFSET] = 0x0f; + vidmem[x*(160+XTRA_OFFSET)+157+XTRA_OFFSET] = 0x0f; + vidmem[x*(160+XTRA_OFFSET)+158+XTRA_OFFSET] = 0x00; + vidmem[x*(160+XTRA_OFFSET)+159+XTRA_OFFSET] = 0xff; + }} +#endif + + /* initialize SDL */ + SDL_Init(SDL_INIT_VIDEO); + + /* set the title bar */ + SDL_WM_SetCaption("SDL Test", "SDL Test"); + + /* create window */ + screen = SDL_SetVideoMode(W, H, 0, 0); + + Verilated::commandArgs(argc, argv); + // init top verilog instance + top = new Vvideo; + +#if DUMP + // init trace dump + Verilated::traceEverOn(true); + tfp = new VerilatedVcdC; + top->trace (tfp, 99); + tfp->open ("video.vcd"); +#endif + + // initialize system inputs + top->clk_32 = 1; + +#if REZ == 0 + // setup palette + unsigned char x,coltab[][3] = { + { 7,7,7 }, { 7,0,0 }, { 0,7,0 }, { 7,7,0 }, { 0,0,7 }, { 7,0,7 }, { 0,7,7 }, { 5,5,5 }, + { 3,3,3 }, { 7,3,3 }, { 3,7,3 }, { 7,7,3 }, { 3,3,7 }, { 7,3,7 }, { 3,7,7 }, { 0,0,0 }}; + for(x=0;x<16;x++) { + top->v__DOT__shifter__DOT__palette_r[x] = coltab[x][0]; + top->v__DOT__shifter__DOT__palette_g[x] = coltab[x][1]; + top->v__DOT__shifter__DOT__palette_b[x] = coltab[x][2]; + } +#elif REZ == 1 + // setup palette + unsigned char x,coltab[][3] = { + { 7,7,7 }, { 7,0,0 }, { 0,7,0 }, { 0,0,0 } }; + for(x=0;x<4;x++) { + top->v__DOT__shifter__DOT__palette_r[x] = coltab[x][0]; + top->v__DOT__shifter__DOT__palette_g[x] = coltab[x][1]; + top->v__DOT__shifter__DOT__palette_b[x] = coltab[x][2]; + } +#endif + +#if 1 + // show OSD + top->v__DOT__osd__DOT__enabled = 1; + { int i, j; + for(i=0;i<2048;i++) + top->v__DOT__osd__DOT__buffer[i] = (i&8)?0xf0:0x0f; + + for(i=0;i<256;i++) { + top->v__DOT__osd__DOT__buffer[i] = 0x33; + top->v__DOT__osd__DOT__buffer[i+2048-256] = 0xcc; + } + + for(i=0;i<8;i++) { + for(j=0;j<4;j++) { + top->v__DOT__osd__DOT__buffer[i*256+j+0] = 0x66; + top->v__DOT__osd__DOT__buffer[i*256+j+4] = 0x00; + top->v__DOT__osd__DOT__buffer[i*256+j+8] = 0xff; + top->v__DOT__osd__DOT__buffer[i*256+j+12] = 0x00; + + top->v__DOT__osd__DOT__buffer[i*256+255-j-12] = 0x00; + top->v__DOT__osd__DOT__buffer[i*256+255-j-8] = 0xff; + top->v__DOT__osd__DOT__buffer[i*256+255-j-4] = 0x00; + top->v__DOT__osd__DOT__buffer[i*256+255-j-0] = 0x66; + } + } + } +#endif + + char adjust_x = -1; + char adjust_y = 1; + + top->adjust = 256*(unsigned char)adjust_x + (unsigned char)adjust_y; + + top->scandoubler_disable = SD?0:1; + top->viking_enable = VIKING?1:0; + top->scanlines = SL; + + // reset + wait_ns(100); + top->cpu_reset = 1; + wait_ns(random()%2000); + top->cpu_reset = 0; + + top->v__DOT__shifter__DOT__hcnt = random(); + +#if (STE_SHIFT != 0) || (STE_LINE_OFFSET != 0) + top->ste = 1; + top->v__DOT__shifter__DOT__pixel_offset = STE_SHIFT; + top->v__DOT__shifter__DOT__line_offset = STE_LINE_OFFSET; +#endif + +#if VIKING + wait_ms(13+14.5); +#else + +#if REZ != 2 + // switch to pal 50 hz lowrez + top->pal56 = PAL56; + top->v__DOT__shifter__DOT__shmode = REZ; // lowrez/mid + top->v__DOT__shifter__DOT__syncmode = PAL?2:0; // pal + +#if PAL +#if PAL56 && SD +#define IMGTIME (612 * 928 * 1000 / CLK) + wait_ms(34+2*IMGTIME); // PAL56 +#else +#define IMGTIME (626 * 1024 * 1000 / CLK) // one full image has 626 lines @ 32us = 40.064 + wait_ms(36+2*IMGTIME); // PAL50 +#endif +#else +#define IMGTIME (526 * 1016 * 1000 / CLK) + wait_ms(31+2*IMGTIME); // NTSC +#endif +#else + // skip forward to first image + wait_ms(12); +#endif +#endif + + // fetch image parameters + // top->v__DOT__shifter__DOT__de_h_end + // top->v__DOT__shifter__DOT__t0_h_border_right + // t1_h_blank_right + // t2_h_sync + // t3_h_blank_left + // t4_h_border_left + // t6_v_border_bot + // t7_v_blank_bot + // t8_v_sync + // t9_v_blank_top + // t10_v_border_top + // t11_v_end + +#if !VIKING +#if REZ==0 +#define PCLK CLK/4 +#elif REZ==1 +#define PCLK CLK/2 +#else +#define PCLK CLK +#endif + printf("Timing:\n"); + printf("Total: %d\n", top->v__DOT__shifter__DOT__t5_h_end+1); + printf("HFreq: %.3fkHz\n", PCLK/1000/(top->v__DOT__shifter__DOT__t5_h_end+1)); + + // v__DOT__shifter__DOT__config_string[2U] +#endif + + printf("DUMP ENABLE\n"); + dump_enabled = 1; + + // verify scan doubler state + // printf("Scandoubler:\n"); + // int total = top->v__DOT__scandoubler__DOT__hs_low + top->v__DOT__scandoubler__DOT__hs_high + 2; + // printf(" Hor total = %d -> %.3fkhz\n", total, CLK/2000.0/total); + + // printf("scan doubler is %s\n", top->v__DOT__scandoubler_enabled?"enabled":"disabled"); + +#if VIKING + wait_ms(14); +#else +#if REZ != 2 +#if PAL +#if PAL56 && SD // PAL56 + wait_ms(18); +#else + wait_ms(21); // PAL50 +#endif +#else + wait_ms(18); // NTSC +#endif +#else + wait_ms(16); +#endif +#endif + +#if DUMP + tfp->close(); +#endif + + getchar(); + + /* cleanup SDL */ + SDL_Quit(); + + exit(0); + } + diff --git a/tests/verilator/video/viking.raw b/tests/verilator/video/viking.raw new file mode 100644 index 0000000000000000000000000000000000000000..baf2249992e4c1d525499acfa5157c2130afd695 GIT binary patch literal 163840 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 Wu8siLLL&rJ6I?oK@2GGzoc}*#ufr4o literal 0 HcmV?d00001 diff --git a/tests/verilator/video/viking.v b/tests/verilator/video/viking.v new file mode 100644 index 0000000..fb25232 --- /dev/null +++ b/tests/verilator/video/viking.v @@ -0,0 +1,155 @@ +// +// viking.v +// +// Atari ST(E) Viking/SM194 +// http://code.google.com/p/mist-board/ +// +// Copyright (c) 2013-2015 Till Harbaum +// +// 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 . + +// The viking card does not have its own CPU interface as it is not +// configurable in any way. It just etches data from ram and displays +// it on screen. + +module viking ( + input pclk, // 128 MHz pixel clock + + // memory interface + input himem, // use memory behind rom + input bclk, // 8 MHz bus clock + input [1:0] bus_cycle, // bus-cycle for bus access sync + output reg [22:0] addr, // video word address + output read, // video read cycle + input [63:0] data, // video data read + + // VGA output (multiplexed with sm124 output in top level) + output hs, + output vs, + output [3:0] r, + output [3:0] g, + output [3:0] b +); + +localparam BASE = 23'h600000; // c00000 +localparam BASE_HI = 23'h740000; // e80000 + +// total width must be multiple of 64, so video runs synchronous +// to main bus + +// Horizontal timing +// HBP1 | H | HFP | HS | HBP2 +// -----|XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|-----|____|----- +// HBP1 is used for prefetch + +// 1280x +localparam H = 1280; +localparam HFP = 88; +localparam HS = 136; +localparam HBP1 = 32; +localparam HBP2 = 192; + +// Vertical timing +// V | VFP | VS | VBP +// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|-----|____|----- + +// x1024 +localparam V = 1024; +localparam VFP = 9; +localparam VS = 4; +localparam VBP = 9; + +assign read = (bus_cycle == 2) && me; // memory enable can directly be used as a ram read signal + +// --------------------------------------------------------------------------- +// --------------------------- internal state counter ------------------------ +// --------------------------------------------------------------------------- + +reg [3:0] t; +always @(posedge pclk) begin + // 128 Mhz counter synchronous to 8 Mhz clock + // force counter to pass state 0 exactly after the rising edge of clk_reg (8Mhz) + if(((t == 4'd15) && ( bclk == 0)) || + ((t == 4'd0) && ( bclk == 1)) || + ((t != 4'd15) && (t != 4'd0))) + t <= t + 4'd1; +end + +// create internal bus_cycle signal which is stable on the positive clock +// edge and extends the previous state by half a 128 Mhz clock cycle +reg [5:0] bus_cycle_L; +always @(negedge pclk) + bus_cycle_L <= { bus_cycle, t }; + + +// --------------- horizontal timing ------------- +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 "video" bus cyle (0) + // cpu has cycles 1 and 3 + if(bus_cycle_L == { 2'd1, 4'd15 }) + h_cnt<=0; + end else + h_cnt <= h_cnt + 1; +end + +// --------------- vertical timing ------------- +reg[10:0] v_cnt; // 0..2047 +assign vs = ((v_cnt >= V+VFP) && (v_cnt < V+VFP+VS))?0:1; +always@(posedge pclk) begin + if(h_cnt==HBP1+H+HFP+HS+HBP2-1) begin + if(v_cnt==V+VFP+VS+VBP-1) v_cnt <= 0; + else v_cnt <= v_cnt+1; + end +end + +reg [63:0] input_latch; +reg [63:0] shift_register; + +// ---------------- memory timing ---------------- +always@(posedge pclk) begin + // last line on screen + if(v_cnt == V+VFP+VS+VBP-2) + addr <= himem?BASE_HI:BASE; + else if(me && bus_cycle_L == 6'h30) // directly after read + addr <= addr + 23'd4; // advance 4 words (64 bits) + + if(me && (bus_cycle_L == 6'h2f)) + input_latch <= data; + + if(bus_cycle_L == 6'h3f) + // 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 + +// memory enable (data is being read from memory) +wire me = (v_cnt < V)&&(h_cnt < H); +// display enable (data is being displayed) +wire de = (v_cnt < V)&&(h_cnt >= HBP1)&&(h_cnt < HBP1+H); + +wire pix = de?(!shift_register[63]):1'b0; + +// 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 diff --git a/tests/verilator/video/xvid.raw b/tests/verilator/video/xvid.raw new file mode 100644 index 0000000000000000000000000000000000000000..4c4311d2d05d8b54caf7509e0112d84df1bfbb52 GIT binary patch literal 393216 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 literal 0 HcmV?d00001