From 105b24ffa41dddae310567f4becbdb22fb9d67a7 Mon Sep 17 00:00:00 2001 From: UzixLS Date: Sat, 15 Aug 2020 16:24:29 +0300 Subject: [PATCH] update firmware 1. quartus 13 may be used for build now; 2. fix small border offset; 3. fix too fast flashing with attribute bit 7. --- README.md | 2 +- cpld/clocks.sdc | 16 ++++ cpld/top.v | 227 +++++++++++++++++++----------------------------- cpld/zx_ula.qsf | 6 +- out/zx_ula.pof | Bin 8012 -> 8023 bytes 5 files changed, 110 insertions(+), 141 deletions(-) create mode 100644 cpld/clocks.sdc diff --git a/README.md b/README.md index df3d1cb..b7ce519 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Specifications: - PAL encoder in CPLD - Intended to build on 9x15 prototyping board, but may be built on manufactured PCB -To modify and compile CPLD firmware you need Quartus 9.0 SP2. +To modify and compile CPLD firmware you need Quartus 13.0sp1. To modify schematic and pcb you need Kicad 5.1.4 or newer. [Some photos](https://cloud.err200.net/index.php/s/73TR85tYZkMm8Ax?path=%2Fsizif-128) diff --git a/cpld/clocks.sdc b/cpld/clocks.sdc new file mode 100644 index 0000000..051b42f --- /dev/null +++ b/cpld/clocks.sdc @@ -0,0 +1,16 @@ +create_clock -period 14.4MHz -name {clk_14mhz} [get_ports {clk14}] +create_clock -period 16MHz -name {clk_16mhz} [get_ports {clk16}] + +# clkcpu 3.5 +create_generated_clock -name {clkcpu} -divide_by 4 -source [get_ports {clk14}] [get_registers {hc0[1]}] + +# int len in turbo = 66 +create_generated_clock -name {n_int} -divide_by 64 -source [get_ports {clk14}] [get_registers {n_int~reg0}] + +# chroma carrier +create_generated_clock -name {chroma_carrier} -divide_by 6 -source [get_ports {clk16}] [get_registers {*:chroma_gen1|carrier[14]}] + +set_false_path -from [get_registers {r~reg0}] -to [get_clocks {clk_16mhz}] +set_false_path -from [get_registers {g~reg0}] -to [get_clocks {clk_16mhz}] +set_false_path -from [get_registers {b~reg0}] -to [get_clocks {clk_16mhz}] +set_false_path -from [get_registers {hsync1}] -to [get_clocks {chroma_carrier}] diff --git a/cpld/top.v b/cpld/top.v index c53901b..5fe0d2a 100755 --- a/cpld/top.v +++ b/cpld/top.v @@ -29,8 +29,8 @@ module zx_ula( input [4:0] kd, input tape_in, - output tape_out, - output beeper, + output reg tape_out, + output reg beeper, output ay_clk, output reg ay_bdir, @@ -44,9 +44,8 @@ module zx_ula( output reg csync ); -wire timings = 0; -wire turbo = 0; -wire [2:0] border; +wire [15:0] xa = {a15, a14, va[13:2], a1, a0}; +wire [7:0] xd = vd; reg n_iorq_delayed; always @(posedge clkcpu) @@ -58,70 +57,25 @@ wire n_iorq0 = n_iorq | n_iorq_delayed; localparam H_AREA = 256; localparam V_AREA = 192; localparam SCREEN_DELAY = 8; +localparam H_TOTAL = 448; +localparam V_TOTAL = 320; -localparam H_LBORDER_S48 = 32 - SCREEN_DELAY; -localparam H_RBORDER_S48 = 64 + SCREEN_DELAY; -localparam H_BLANK1_S48 = 16; -localparam H_SYNC_S48 = 32; -localparam H_BLANK2_S48 = 48; -localparam H_TOTAL_S48 = H_AREA + H_RBORDER_S48 + H_BLANK1_S48 + H_SYNC_S48 + H_BLANK2_S48 + H_LBORDER_S48; -localparam V_BBORDER_S48 = 56; -localparam V_SYNC_S48 = 8; -localparam V_TBORDER_S48 = 56; -localparam V_TOTAL_S48 = V_AREA + V_BBORDER_S48 + V_SYNC_S48 + V_TBORDER_S48; +reg [`CLOG2(V_TOTAL)-1:0] vc; +reg [`CLOG2(H_TOTAL):0] hc0; +wire [`CLOG2(H_TOTAL)-1:0] hc = hc0[$bits(hc0)-1:1]; -localparam H_LBORDER_S128 = 48 - SCREEN_DELAY; -localparam H_RBORDER_S128 = 48 + SCREEN_DELAY; -localparam H_BLANK1_S128 = 28; -localparam H_SYNC_S128 = 32; -localparam H_BLANK2_S128 = 44; -localparam H_TOTAL_S128 = H_AREA + H_RBORDER_S128 + H_BLANK1_S128 + H_SYNC_S128 + H_BLANK2_S128 + H_LBORDER_S128; -localparam V_BBORDER_S128 = 56; -localparam V_SYNC_S128 = 8; -localparam V_TBORDER_S128 = 55; -localparam V_TOTAL_S128 = V_AREA + V_BBORDER_S128 + V_SYNC_S128 + V_TBORDER_S128; +wire hc0_reset = hc0 == (H_TOTAL<<1) - 1'b1 ; +wire vc_reset = vc == V_TOTAL - 1'b1 ; +wire hsync0 = hc[8:5] == 4'b1010; +wire vsync0 = vc[7:3] == 5'b11111; +wire blank = (vc[7:3] == 5'b11111) || (hc[8:6] == 3'b101); -localparam H_LBORDER_PENT = 64 - SCREEN_DELAY; -localparam H_RBORDER_PENT = 64 + SCREEN_DELAY; -localparam H_BLANK1_PENT = 0; -localparam H_SYNC_PENT = 32; -localparam H_BLANK2_PENT = 32; -localparam H_TOTAL_PENT = H_AREA + H_RBORDER_PENT + H_BLANK1_PENT + H_SYNC_PENT + H_BLANK2_PENT + H_LBORDER_PENT; -localparam V_BBORDER_PENT = 56; -localparam V_SYNC_PENT = 8; -localparam V_TBORDER_PENT = 64; -localparam V_TOTAL_PENT = V_AREA + V_BBORDER_PENT + V_SYNC_PENT + V_TBORDER_PENT; +reg hsync1; +always @(posedge clk14) + hsync1 = hc[8:5] != 4'b1010; -reg [`CLOG2(`MAX(V_TOTAL_S128, V_TOTAL_PENT))-1:0] vc; -reg [`CLOG2(`MAX(H_TOTAL_S128, H_TOTAL_PENT)):0] hc0; -wire [`CLOG2(`MAX(H_TOTAL_S128, H_TOTAL_PENT))-1:0] hc = hc0[$bits(hc0)-1:1]; - -wire hc0_reset = timings? - hc0 == (H_TOTAL_S128<<1) - 1'b1 : - hc0 == (H_TOTAL_PENT<<1) - 1'b1 ; -wire vc_reset = timings? - vc == V_TOTAL_S128 - 1'b1 : - vc == V_TOTAL_PENT - 1'b1 ; -wire hsync0 = timings? - (hc >= (H_AREA + H_RBORDER_S128 + H_BLANK1_S128)) && - (hc < (H_AREA + H_RBORDER_S128 + H_BLANK1_S128 + H_SYNC_S128)) : - (hc[8:5] == 4'b1010); -wire vsync0 = timings? - (vc >= (V_AREA + V_BBORDER_S128)) && (vc < (V_AREA + V_BBORDER_S128 + V_SYNC_S128)) : - (vc[7:3] == 5'b11111) ; -wire blank = timings? - ((vc >= (V_AREA + V_BBORDER_S128)) && (vc < (V_AREA + V_BBORDER_S128 + V_SYNC_S128))) || - ((hc >= (H_AREA + H_RBORDER_S128)) && - (hc < (H_AREA + H_RBORDER_S128 + H_BLANK1_S128 + H_SYNC_S128 + H_BLANK2_S128))) : - ((vc[7:3] == 5'b11111) || - (hc[8:6] == 3'b101)); - -always @(posedge clk14 or negedge rst_n) begin - if (!rst_n) begin - hc0 <= 0; - vc <= 0; - end - else if (hc0_reset) begin +always @(posedge clk14) begin + if (hc0_reset) begin hc0 <= 0; if (vc_reset) begin vc <= 0; @@ -135,7 +89,7 @@ always @(posedge clk14 or negedge rst_n) begin end end -reg [3:0] blink_cnt; +reg [4:0] blink_cnt; wire blink = blink_cnt[$bits(blink_cnt)-1]; always @(posedge n_int or negedge rst_n) begin if (!rst_n) @@ -144,20 +98,8 @@ always @(posedge n_int or negedge rst_n) begin blink_cnt <= blink_cnt + 1'b1; end +reg [2:0] border; reg [7:0] bitmap, attr, bitmap_next, attr_next; -wire pixel = bitmap[7]; -always @(posedge clk14) begin - if (hc0[0]) begin - if (blank) - {i, g, r, b} = 4'b0000; - else begin - {g, r, b} = (pixel ^ (attr[7] & blink))? attr[2:0] : attr[5:3]; - i = (g | r | b) & attr[6]; - end - csync <= ~(vsync0 ^ hsync0); - end -end - reg screen_read; wire attr_read = screen_read & ~hc0[0]; wire bitmap_read = screen_read & hc0[0]; @@ -167,8 +109,7 @@ wire [14:0] screen_addr = attr_read? attr_addr : bitmap_addr; wire screen_load = (vc < V_AREA) && (hc < H_AREA); wire screen_show = (vc < V_AREA) && (hc >= SCREEN_DELAY) && (hc < H_AREA + SCREEN_DELAY); wire screen_update = hc0[3:0] == 4'b1111; -wire border_update = ((screen_load == 0 && screen_show == 0) || (screen_load == 0 && hc0[3:0] == 4'b1111) || (screen_show == 0 && timings == 0)) - && (timings == 0 || hc0[3:0] == 4'b1111); +wire border_update = (screen_load == 0 && hc0[3:0] == 4'b1111) || (screen_show == 0); always @(posedge clk14 or negedge rst_n) begin if (!rst_n) begin @@ -198,66 +139,76 @@ always @(posedge clk14 or negedge rst_n) begin end end - -wire [15:0] xa = {a15, a14, va[13:2], a1, a0}; - -/* PORT #FE */ -wire port_fe_cs = n_m1 == 1 && n_iorq0 == 0 && xa[0] == 0; -reg port_fe_rd; -always @(posedge clk14) - port_fe_rd <= port_fe_cs && n_rd == 0; - -wire [7:0] port_fe_data = {1'b1, tape_in, 1'b1, kd[4:0]}; -reg [7:0] port_fe; -assign beeper = port_fe[4]; -assign tape_out = port_fe[3] ^ tape_in; -assign border = port_fe[2:0]; -always @(posedge clk14 or negedge rst_n) begin - if (!rst_n) - port_fe <= 0; - else if (port_fe_cs && n_wr == 0) - port_fe <= vd; -end - - -/* PORT #7FFD */ -wire port_7ffd_cs = n_m1 == 1 && n_iorq0 == 0 && xa[1] == 0 && xa[15] == 0; -reg [7:0] port_7ffd; -wire [2:0] rambank = port_7ffd[2:0]; -wire vbank = port_7ffd[3]; -wire rombank = port_7ffd[4]; -wire lock_7ffd = port_7ffd[5]; -always @(posedge clk14 or negedge rst_n) begin - if (!rst_n) - port_7ffd <= 0; - else if (port_7ffd_cs && n_wr == 0 && lock_7ffd == 0) - port_7ffd <= vd; -end - - -/* INT GENERATOR */ -localparam INT_V_S48 = 248; -localparam INT_H_FROM_S48 = 0; -localparam INT_H_TO_S48 = 63; -localparam INT_V_S128 = 248; -localparam INT_H_FROM_S128 = 2; -localparam INT_H_TO_S128 = 65; -localparam INT_V_PENT = 239; -localparam INT_H_FROM_PENT = 320; -localparam INT_H_TO_PENT = 384; +wire pixel = bitmap[7]; always @(posedge clk14) begin - n_int <= timings? - (vc != INT_V_S128 || hc < INT_H_FROM_S128 || hc > INT_H_TO_S128) : - (vc != INT_V_PENT || hc < INT_H_FROM_PENT || hc > INT_H_TO_PENT) ; + if (hc0[0]) begin + if (blank) + {i, g, r, b} = 4'b0000; + else begin + {g, r, b} = (pixel ^ (attr[7] & blink))? attr[2:0] : attr[5:3]; + i = (g | r | b) & attr[6]; + end + csync <= ~(vsync0 ^ hsync0); + end end /* CLOCK */ -reg [2:0] clk_cnt; -always @(posedge clk14) begin - clk_cnt <= clk_cnt + 1'b1; +assign clkcpu = hc[0]; + + +/* INT GENERATOR */ +localparam INT_V = 239; +localparam INT_H_FROM = 318; +localparam INT_H_TO = 382; +always @(posedge clk14 or negedge rst_n) begin + if (!rst_n) + n_int <= 1; + else + n_int <= vc != INT_V || hc < INT_H_FROM || hc > INT_H_TO ; +end + + +/* PORT #FE */ +wire port_fe_cs = n_m1 == 1 && n_iorq0 == 0 && xa[0] == 0; + +wire [7:0] port_fe_data = {1'b1, tape_in, 1'b1, kd[4:0]}; +reg port_fe_rd; +always @(posedge clk14) + port_fe_rd <= port_fe_cs && n_rd == 0; + +always @(posedge clk14 or negedge rst_n) begin + if (!rst_n) begin + beeper <= 0; + tape_out <= 0; + border <= 0; + end + else if (port_fe_cs && n_wr == 0) begin + beeper <= xd[4]; + tape_out <= xd[3]; + border <= xd[2:0]; + end +end + + +/* PORT #7FFD */ +wire port_7ffd_cs = n_m1 == 1 && n_iorq0 == 0 && xa[1] == 0 && xa[15] == 0 && xa[14] == 1; +reg [2:0] rambank; +reg rombank, vbank, lock_7ffd; +always @(posedge clk14 or negedge rst_n) begin + if (!rst_n) begin + rambank <= 0; + vbank <= 0; + rombank <= 0; + lock_7ffd <= 0; + end + else if (port_7ffd_cs && n_wr == 0 && lock_7ffd == 0) begin + rambank <= xd[2:0]; + vbank <= xd[3]; + rombank <= xd[4]; + lock_7ffd <= xd[5]; + end end -assign clkcpu = turbo? clk_cnt[0] : clk_cnt[1]; /* AY */ @@ -271,7 +222,7 @@ always @(posedge clkcpu or negedge rst_n) begin ay_bdir <= xa[15] == 1 && xa[1] == 0 && n_m1 == 1 && n_iorq0 == 0 && n_wr == 0; end end -assign ay_clk = clk_cnt[2]; +assign ay_clk = hc[1]; /* VIDEO */ @@ -279,7 +230,7 @@ reg [2:0] chroma0; chroma_gen chroma_gen1( .cg_clock(clk16), .cg_rgb({g,r,b}), - .cg_hsync(~hsync0), + .cg_hsync(hsync1), .cg_enable(1'b1), .cg_pnsel(1'b0), .cg_out(chroma0) @@ -305,7 +256,7 @@ assign chroma[1] = chroma0[2]? chroma0[0] : 1'bz; // 1 00 1 rom1 wire n_vcs_cpu = n_mreq | ~(a15 | a14); -assign n_vrd = ((n_vcs_cpu == 0 && n_rd == 0) || screen_read == 1)? 1'b0 : 1'b1 ; +assign n_vrd = ((n_vcs_cpu == 0 && n_rd == 0) || screen_read == 1)? 1'b0 : 1'b1; assign n_vwr = ((n_vcs_cpu == 0 && n_wr == 0) && screen_read == 0)? 1'b0 : 1'b1; always @(posedge clkcpu) begin n_romcs <= n_mreq | a15 | a14; diff --git a/cpld/zx_ula.qsf b/cpld/zx_ula.qsf index 1703150..c0e2c9c 100755 --- a/cpld/zx_ula.qsf +++ b/cpld/zx_ula.qsf @@ -41,7 +41,7 @@ set_global_assignment -name DEVICE "EPM7128SLC84-15" set_global_assignment -name TOP_LEVEL_ENTITY zx_ula set_global_assignment -name ORIGINAL_QUARTUS_VERSION "9.0 SP2" set_global_assignment -name PROJECT_CREATION_TIME_DATE "08:15:12 APRIL 28, 2019" -set_global_assignment -name LAST_QUARTUS_VERSION "9.0 SP2" +set_global_assignment -name LAST_QUARTUS_VERSION "13.0 SP1" set_global_assignment -name USE_GENERATED_PHYSICAL_CONSTRAINTS OFF -section_id eda_blast_fpga set_global_assignment -name DEVICE_FILTER_SPEED_GRADE 15 set_global_assignment -name MIN_CORE_JUNCTION_TEMP 0 @@ -92,7 +92,7 @@ set_location_assignment PIN_49 -to vd[1] set_location_assignment PIN_50 -to vd[0] set_global_assignment -name VERILOG_INPUT_VERSION SYSTEMVERILOG_2005 set_global_assignment -name VERILOG_SHOW_LMF_MAPPING_MESSAGES OFF -set_global_assignment -name MAX7000_OPTIMIZATION_TECHNIQUE AREA +set_global_assignment -name MAX7000_OPTIMIZATION_TECHNIQUE BALANCED set_global_assignment -name FMAX_REQUIREMENT "14 MHz" set_global_assignment -name FMAX_REQUIREMENT "14 MHz" -section_id clk14 set_location_assignment PIN_6 -to i @@ -130,6 +130,8 @@ set_global_assignment -name SAVE_DISK_SPACE OFF set_global_assignment -name FIT_ONLY_ONE_ATTEMPT OFF set_global_assignment -name SLOW_SLEW_RATE OFF set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output/ +set_global_assignment -name SYNTH_TIMING_DRIVEN_SYNTHESIS OFF +set_global_assignment -name SDC_FILE clocks.sdc set_global_assignment -name VHDL_FILE chroma_gen16.vhd set_global_assignment -name VERILOG_FILE top.v set_global_assignment -name VERILOG_INCLUDE_FILE util.vh \ No newline at end of file diff --git a/out/zx_ula.pof b/out/zx_ula.pof index e0c4c04113438d03dbcbf6bf9a587782cccdfa56..ad63c0c0f95ac97dad389a208c28cc7281b833a3 100644 GIT binary patch literal 8023 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 literal 8012 zcmb_hPi!4W8GnH`vZYiwB!`GY^@@b098B!sIO)l%t5gKlBFHKnTCnrSq84r~ISDzG z=P6EA^%!wLh#X7=>VZf&Mk1Do;wc1fAcB1GQ!96>UMgWZTM)^T?AsZB-#0V6v+wQZ z#Z||CJ2T(>e&6@a_sz`i+jsKYFVt$aUA5hOeF@j^TsSp<{=&KXi4*mc^K(C(KlR#c z)ARN3PS2m4nR~td?AHhDV;5#-&(;rr<4ApQCG`$DY`s*j%>KRR^y*u;y^9~&MRI#T;$tryalUOzu`es=n7 z?Mc9>?5+LwX>Q;W{g=Gk(#Dtsf3{E$#bZ#XkrfrCnQW&G8Rz<#D~u6jUK$!ek1mQT z6v6n`V!2|i%_>qBW28gAis-`lI9^Qn2jQChEt$6 zK7>WtjBTA=+x6Okg4Bit#I{^8RIqlZu0V6L&!Z~W+b-3Lrzt7?c}R3eNF>ICkWkN- z&b+0{kF_NL5yjB&F{-I4&A(g|*8UiV1Y!X2|1nxrKCrnUTT5Bq%vML<2)mr+je2>3 zx?u8;y#tCgsB3htNtr-hbdhY`a|Z8IEs_6QPQaBc_#hL^agJ$zB|jio7aD8IFOC|_ zEguVK97qvc3MDZ^8m8(>DD|>J9RePM<`&70tdWb$YqSbNnoPu&*;!E3Qcz{`!0#-XzD3`Q34wG?WJ5QXmIB=ko@$8 z5oDMZkuqaw|Cd`+@G~Vy8^G%-p%EM=HggjS5;9@EM#GnOK{?H$L#*@2t6|N;E`5WQ zCbOwJMETs}l=Ucp;#B*9KnfMMq$#Y%3Y}Od)Fd&}q;*FFAQ+yc1h7mEWhNn7!T@5? zi482%66_%qg%6tE8cU_`5rB#)jiepJ`YEQx#)YmT%@dHarHhcp7Qk$7vOCH^?}@3pZ~4`C(`@i76BtQJR zg+vj-+AG&vE|oNSm*YGR);M3wX&6dBgEi9VW*6w~nt%WsG}%3SDVG|~8|-15B5#tg zExJqlyd|{2JJv7J9F}MtRcLTD`kv)z{FRFUS#b{&YhQeUcQ97O^Z0Q{8OA@#bbW^1rmpk;t^sVSUX+2Pmi)q6? ztsRDhQbxJJZIGG@@y+gt>`Sr7BDzq9;38h7hE8U~6;h+`=9#}6WYZJNm6?3dkx564bgvW4L zqwNGOemiM45zsiJg5R9OCtm_U?n2ev97z@?uLL5c8#ERub5O2?%6frimN}ah%Znkh zO>fs*N36i(9N?N8^p9;7P67){uGqVX?{Qs4!=SZM4k(x6D~Zaqv#ZWes_0Wi>CzIwn^w#Uqg|Ai}pTwL4}jbjTcl z%`Oi7`fI~~x$)`K?~hHqczNKNo{=|y)BN(!PQTo&@N2Bn9Kwdq@7X`@j=druOZ*dg9+l-fH$Y_g|vUz{tTH z&3BupFHQB$t~__W_eQhcJpJpS$uBg;M;dTY3{!=HSxF0<3qjg z-s%6v*rln7J&Oas>V5l8|ADcIS8w-R9bf4kIo|jF6IVVu_sWVkw6P{*TA%lX*~E8$ z{LT^MZB;sHHV}es6B}jH2PT@t+d8v=KR^W?+sM9F0L66cjZ+1jiUsH!Xrr?cVUcL5 zVw{-*bR##7RB5aw1Tpb6sktZkh0ZOb6s#*@K7c}Kz*9Ta@cyM8Pb&*ICj7pH7gms? zz{yVC|!W6I1!j3DMAmfD{x>P&PftZ!s%G0B#s3na3H}P z66o0^J&46cUhUh$`~V>!uMsqw1Ke#0{PGB@AfymNE!lt&4>CbaZI)mMAZ6%nrh*7w zE2ZOvFor6agV*%^WYtcb;zB_{CCLzvr7=7s)eRaWEeKt#9tdrSB`_5Vt5OJZ3T1RU zLYQ(I;ft0SOEB9AStkW*(!~FeKY?Q^9#^BO8cxkLeY~2QNbkFV4)_VMRnCju*tq&giKdR>j+5i9m