diff --git a/TSConf.sdc b/TSConf.sdc index 48f08b5..ffb8f38 100644 --- a/TSConf.sdc +++ b/TSConf.sdc @@ -36,6 +36,7 @@ set_false_path -to [get_ports {AUDIO_L}] set_false_path -to [get_ports {AUDIO_R}] set_false_path -to [get_ports {LED}] +set_false_path -to [get_ports {UART_TX}] set_false_path -from [get_ports {UART_RX}] set_multicycle_path -from {tsconf|CPU|*} -setup 2 diff --git a/TSConf.srf b/TSConf.srf index 9347723..34e5728 100644 --- a/TSConf.srf +++ b/TSConf.srf @@ -1,6 +1,6 @@ { "" "" "" "Verilog HDL or VHDL warning at user_io.v(271): object \"kbd_out_data_available\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} -{ "" "" "" "Verilog HDL or VHDL warning at tsconf.v(111): object \"cfg_tape_sound\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} -{ "" "" "" "Verilog HDL or VHDL warning at tsconf.v(132): object \"zclk\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} +{ "" "" "" "Verilog HDL or VHDL warning at tsconf.v(113): object \"cfg_tape_sound\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} +{ "" "" "" "Verilog HDL or VHDL warning at tsconf.v(134): object \"zclk\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "Verilog HDL or VHDL warning at video_out.v(83): object \"red0\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "Verilog HDL or VHDL warning at video_out.v(84): object \"grn0\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "Verilog HDL or VHDL warning at video_out.v(85): object \"blu0\" assigned a value but never read" { } { } 0 10036 "" 0 0 "Quartus II" 0 -1 0 ""} @@ -22,3 +22,6 @@ { "" "" "" "Verilog HDL information at jt12_reg_ch.v(104): always construct contains both blocking and non-blocking assignments" { } { } 0 10268 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "PCI-clamp diode is not supported in this mode. The following 1 pins must meet the Altera requirements for 3.3V, 3.0V, and 2.5V interfaces if they are connected to devices other than the supported configuration devices. In these cases, Altera recommends termination method as specified in the Application Note 447." { } { } 0 169203 "" 0 0 "Quartus II" 0 -1 0 ""} { "" "" "" "26 pins must meet Altera requirements for 3.3-, 3.0-, and 2.5-V interfaces. For more information, refer to AN 447: Interfacing Cyclone III Devices with 3.3/3.0/2.5-V LVTTL/LVCMOS I/O Systems." { } { } 0 169177 "" 0 0 "Quartus II" 0 -1 0 ""} +{ "" "" "" "Verilog HDL Casex/Casez warning at zifi.v(112): casex/casez item expression overlaps with a previous casex/casez item expression" { } { } 0 10935 "" 0 0 "Quartus II" 0 -1 0 ""} +{ "" "" "" "Verilog HDL Casex/Casez warning at zifi.v(143): casex/casez item expression overlaps with a previous casex/casez item expression" { } { } 0 10935 "" 0 0 "Quartus II" 0 -1 0 ""} +{ "" "" "" "Verilog HDL Expression warning at zifi.v(199): truncated literal to match 8 bits" { } { } 0 10229 "" 0 0 "Quartus II" 0 -1 0 ""} diff --git a/TSConf.sv b/TSConf.sv index 0072020..23448c0 100644 --- a/TSConf.sv +++ b/TSConf.sv @@ -295,7 +295,7 @@ wire [7:0] R,G,B; wire VS, HS; wire [15:0] SOUND_L; wire [15:0] SOUND_R; -wire tape_out, midi_out; +wire tape_out, midi_out, uart_out; tsconf tsconf ( @@ -334,6 +334,8 @@ tsconf tsconf .TAPE_IN(UART_RX), .TAPE_OUT(tape_out), .MIDI_OUT(midi_out), + .UART_RX(UART_RX), + .UART_TX(uart_out), .CFG_OUT0(st_out0), .CFG_60HZ(st_60hz), @@ -362,6 +364,7 @@ tsconf tsconf reg uart_tx = 1'b1; reg tape_out_old = 1'b0; reg midi_out_old = 1'b0; +reg uart_out_old = 1'b0; always @(posedge clk_sys) begin if (tape_out_old != tape_out) begin @@ -372,6 +375,10 @@ always @(posedge clk_sys) begin midi_out_old <= midi_out; uart_tx <= midi_out; end + if (uart_out_old != uart_out) begin + uart_out_old <= uart_out; + uart_tx <= uart_out; + end end assign UART_TX = uart_tx; diff --git a/files.qip b/files.qip index 3cdbb03..9771ad0 100644 --- a/files.qip +++ b/files.qip @@ -10,6 +10,9 @@ set_global_assignment -name VERILOG_FILE rtl/periph/kempston_mouse.v set_global_assignment -name VERILOG_FILE rtl/periph/keyboard.v set_global_assignment -name VERILOG_FILE rtl/periph/mc146818a.v set_global_assignment -name VERILOG_FILE rtl/periph/vdac.v +set_global_assignment -name VERILOG_FILE rtl/periph/zifi.v +set_global_assignment -name VERILOG_FILE rtl/periph/uart_tx.v +set_global_assignment -name VERILOG_FILE rtl/periph/uart_rx.v set_global_assignment -name VHDL_FILE rtl/sound/soundrive.vhd set_global_assignment -name QIP_FILE rtl/sound/jt12/jt03.qip set_global_assignment -name SYSTEMVERILOG_FILE rtl/sound/turbosound.sv diff --git a/rtl/periph/uart_rx.v b/rtl/periph/uart_rx.v new file mode 100644 index 0000000..78133a8 --- /dev/null +++ b/rtl/periph/uart_rx.v @@ -0,0 +1,149 @@ +////////////////////////////////////////////////////////////////////// +// File Downloaded from http://www.nandland.com +////////////////////////////////////////////////////////////////////// +// This file contains the UART Receiver. This receiver is able to +// receive 8 bits of serial data, one start bit, one stop bit, +// and no parity bit. When receive is complete o_rx_dv will be +// driven high for one clock cycle. +// +// Set Parameter CLKS_PER_BIT as follows: +// CLKS_PER_BIT = (Frequency of i_Clock)/(Frequency of UART) +// Example: 10 MHz Clock, 115200 baud UART +// (10000000)/(115200) = 87 + +module uart_rx + #(parameter CLKS_PER_BIT) + ( + input i_Clock, + input i_Rx_Serial, + output o_Rx_DV, + output [7:0] o_Rx_Byte + ); + + localparam s_IDLE = 3'b000; + localparam s_RX_START_BIT = 3'b001; + localparam s_RX_DATA_BITS = 3'b010; + localparam s_RX_STOP_BIT = 3'b011; + localparam s_CLEANUP = 3'b100; + + reg r_Rx_Data_R = 1'b1; + reg r_Rx_Data = 1'b1; + + reg [7:0] r_Clock_Count = 0; + reg [2:0] r_Bit_Index = 0; //8 bits total + reg [7:0] r_Rx_Byte = 0; + reg r_Rx_DV = 0; + reg [2:0] r_SM_Main = 0; + + // Purpose: Double-register the incoming data. + // This allows it to be used in the UART RX Clock Domain. + // (It removes problems caused by metastability) + always @(posedge i_Clock) + begin + r_Rx_Data_R <= i_Rx_Serial; + r_Rx_Data <= r_Rx_Data_R; + end + + + // Purpose: Control RX state machine + always @(posedge i_Clock) + begin + + case (r_SM_Main) + s_IDLE : + begin + r_Rx_DV <= 1'b0; + r_Clock_Count <= 0; + r_Bit_Index <= 0; + + if (r_Rx_Data == 1'b0) // Start bit detected + r_SM_Main <= s_RX_START_BIT; + else + r_SM_Main <= s_IDLE; + end + + // Check middle of start bit to make sure it's still low + s_RX_START_BIT : + begin + if (r_Clock_Count == (CLKS_PER_BIT-1)/2) + begin + if (r_Rx_Data == 1'b0) + begin + r_Clock_Count <= 0; // reset counter, found the middle + r_SM_Main <= s_RX_DATA_BITS; + end + else + r_SM_Main <= s_IDLE; + end + else + begin + r_Clock_Count <= r_Clock_Count + 1'd1; + r_SM_Main <= s_RX_START_BIT; + end + end // case: s_RX_START_BIT + + + // Wait CLKS_PER_BIT-1 clock cycles to sample serial data + s_RX_DATA_BITS : + begin + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1'd1; + r_SM_Main <= s_RX_DATA_BITS; + end + else + begin + r_Clock_Count <= 0; + r_Rx_Byte[r_Bit_Index] <= r_Rx_Data; + + // Check if we have received all bits + if (r_Bit_Index < 7) + begin + r_Bit_Index <= r_Bit_Index + 1'd1; + r_SM_Main <= s_RX_DATA_BITS; + end + else + begin + r_Bit_Index <= 0; + r_SM_Main <= s_RX_STOP_BIT; + end + end + end // case: s_RX_DATA_BITS + + + // Receive Stop bit. Stop bit = 1 + s_RX_STOP_BIT : + begin + // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1'd1; + r_SM_Main <= s_RX_STOP_BIT; + end + else + begin + r_Rx_DV <= 1'b1; + r_Clock_Count <= 0; + r_SM_Main <= s_CLEANUP; + end + end // case: s_RX_STOP_BIT + + + // Stay here 1 clock + s_CLEANUP : + begin + r_SM_Main <= s_IDLE; + r_Rx_DV <= 1'b0; + end + + + default : + r_SM_Main <= s_IDLE; + + endcase + end + + assign o_Rx_DV = r_Rx_DV; + assign o_Rx_Byte = r_Rx_Byte; + +endmodule // uart_rx diff --git a/rtl/periph/uart_tx.v b/rtl/periph/uart_tx.v new file mode 100644 index 0000000..f95e057 --- /dev/null +++ b/rtl/periph/uart_tx.v @@ -0,0 +1,146 @@ +////////////////////////////////////////////////////////////////////// +// File Downloaded from http://www.nandland.com +////////////////////////////////////////////////////////////////////// +// This file contains the UART Transmitter. This transmitter is able +// to transmit 8 bits of serial data, one start bit, one stop bit, +// and no parity bit. When transmit is complete o_Tx_done will be +// driven high for one clock cycle. +// +// Set Parameter CLKS_PER_BIT as follows: +// CLKS_PER_BIT = (Frequency of i_Clock)/(Frequency of UART) +// Example: 10 MHz Clock, 115200 baud UART +// (10000000)/(115200) = 87 + +module uart_tx + #(parameter CLKS_PER_BIT) + ( + input i_Clock, + input i_Tx_DV, + input [7:0] i_Tx_Byte, + output o_Tx_Active, + output reg o_Tx_Serial, + output o_Tx_Done + ); + + localparam s_IDLE = 3'b000; + localparam s_TX_START_BIT = 3'b001; + localparam s_TX_DATA_BITS = 3'b010; + localparam s_TX_STOP_BIT = 3'b011; + localparam s_CLEANUP = 3'b100; + + reg [2:0] r_SM_Main = 0; + reg [7:0] r_Clock_Count = 0; + reg [2:0] r_Bit_Index = 0; + reg [7:0] r_Tx_Data = 0; + reg r_Tx_Done = 0; + reg r_Tx_Active = 0; + + always @(posedge i_Clock) + begin + + case (r_SM_Main) + s_IDLE : + begin + o_Tx_Serial <= 1'b1; // Drive Line High for Idle + r_Tx_Done <= 1'b0; + r_Clock_Count <= 0; + r_Bit_Index <= 0; + + if (i_Tx_DV == 1'b1) + begin + r_Tx_Active <= 1'b1; + r_Tx_Data <= i_Tx_Byte; + r_SM_Main <= s_TX_START_BIT; + end + else + r_SM_Main <= s_IDLE; + end // case: s_IDLE + + + // Send out Start Bit. Start bit = 0 + s_TX_START_BIT : + begin + o_Tx_Serial <= 1'b0; + + // Wait CLKS_PER_BIT-1 clock cycles for start bit to finish + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1'd1; + r_SM_Main <= s_TX_START_BIT; + end + else + begin + r_Clock_Count <= 0; + r_SM_Main <= s_TX_DATA_BITS; + end + end // case: s_TX_START_BIT + + + // Wait CLKS_PER_BIT-1 clock cycles for data bits to finish + s_TX_DATA_BITS : + begin + o_Tx_Serial <= r_Tx_Data[r_Bit_Index]; + + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1'd1; + r_SM_Main <= s_TX_DATA_BITS; + end + else + begin + r_Clock_Count <= 0; + + // Check if we have sent out all bits + if (r_Bit_Index < 7) + begin + r_Bit_Index <= r_Bit_Index + 1'd1; + r_SM_Main <= s_TX_DATA_BITS; + end + else + begin + r_Bit_Index <= 0; + r_SM_Main <= s_TX_STOP_BIT; + end + end + end // case: s_TX_DATA_BITS + + + // Send out Stop bit. Stop bit = 1 + s_TX_STOP_BIT : + begin + o_Tx_Serial <= 1'b1; + + // Wait CLKS_PER_BIT-1 clock cycles for Stop bit to finish + if (r_Clock_Count < CLKS_PER_BIT-1) + begin + r_Clock_Count <= r_Clock_Count + 1'd1; + r_SM_Main <= s_TX_STOP_BIT; + end + else + begin + r_Tx_Done <= 1'b1; + r_Clock_Count <= 0; + r_SM_Main <= s_CLEANUP; + r_Tx_Active <= 1'b0; + end + end // case: s_Tx_STOP_BIT + + + // Stay here 1 clock + s_CLEANUP : + begin + r_Tx_Done <= 1'b1; + r_SM_Main <= s_IDLE; + end + + + default : + r_SM_Main <= s_IDLE; + + endcase + end + + assign o_Tx_Active = r_Tx_Active; + assign o_Tx_Done = r_Tx_Done; + +endmodule diff --git a/rtl/periph/zifi.v b/rtl/periph/zifi.v new file mode 100644 index 0000000..4440b35 --- /dev/null +++ b/rtl/periph/zifi.v @@ -0,0 +1,252 @@ +module zifi +( + input clk, + input rst, + + input wire [ 7:0] din, + output reg [ 7:0] dout, + output reg dataout = 0, + input wire [15:0] a, + + input iord, + input iord_s, + input iowr_s, + + input rx, + output tx +); + + /*-------------------------------------------------------------------------------- + https://github.com/HackerVBI/ZiFi/blob/master/_esp/upd1/README!!__eRS232.txt + + Address Mode Name Description + 0x00EF..0xBFEF R DR Data register (ZIFI or RS232). + Get byte from input FIFO. + Input FIFO must not be empty (xx_IFR > 0). + 0x00EF..0xBFEF W DR Data register (ZIFI or RS232). + Put byte into output FIFO. + Output FIFO must not be full (xx_OFR > 0). + + Address Mode Name Description + 0xC0EF R ZF_IFR ZIFI Input FIFO Used Register. Switch DR to ZIFI FIFO. + 0 - input FIFO is empty, 191 - input FIFO contain 191 or more bytes. + 0xC1EF R ZF_OFR ZIFI Output FIFO Free Register. Switch DR to ZIFI FIFO. + 0 - output FIFO is full, 191 - output FIFO free 191 or more bytes. + 0xC2EF R RS_IFR RS232 Input FIFO Used Register. Switch DR to RS232 FIFO. + 0 - input FIFO is empty, 191 - input FIFO contain 191 or more bytes. + 0xC3EF R RS_OFR RS232 Output FIFO Free Register. Switch DR to RS232 FIFO. + 0 - output FIFO is full, 191 - output FIFO free 191 or more bytes. + + Address Mode Name Description + 0xC7EF W CR Command register. Command set depends on API mode selected. + + All mode commands: + Code Command Description + 000000oi Clear ZIFI FIFOs + i: 1 - clear input ZIFI FIFO, + o: 1 - clear output ZIFI FIFO. + 000001oi Clear RS232 FIFOs + i: 1 - clear input RS232 FIFO, + o: 1 - clear output RS232 FIFO. + 11110mmm Set API mode or disable API: + 0 API disabled. + 1 transparent: all data is sent/received to/from external UART directly. + 2..7 reserved. + 11111111 Get Version Returns highest supported API version. ER=0xFF - no API available. + + Address Mode Name Description + 0xC7EF R ER Error register - command execution result code. Depends on command issued. + + All mode responses: + Code Description + 0x00 OK - no error. + 0xFF REJ - command rejected. + + --------------------------------------------------------------------------------*/ + + + localparam DR = 16'h??EF; + localparam ZF_IFR = 16'hC0EF; + localparam ZF_OFR = 16'hC1EF; + localparam RS_IFR = 16'hC2EF; + localparam RS_OFR = 16'hC3EF; + localparam CR = 16'hC7EF; + localparam ER = 16'hC7EF; + + reg [7:0] er; + reg zifi_en; + + always @(posedge clk) begin + dataout <= dataout & iord; + fifo_rx_rdreq <= 1'b0; + fifo_tx_wrreq <= 1'b0; + fifo_rx_sclr <= 1'b0; + fifo_tx_sclr <= 1'b0; + + if (iord_s) begin + casez (a) + ZF_IFR: begin + dataout <= 1'b1; + dout <= rx_busy? 8'd0 : ((fifo_rx_usedw < 191)? fifo_rx_usedw[7:0] : 8'd191); + zifi_en <= 1'b1; + end + ZF_OFR: begin + dataout <= 1'b1; + dout <= (fifo_tx_freew < 191)? fifo_tx_freew[7:0] : 8'd191; + zifi_en <= 1'b1; + end + RS_IFR: begin + dataout <= 1'b1; + dout <= 8'd0; + zifi_en <= 1'b0; + end + RS_OFR: begin + dataout <= 1'b1; + dout <= 8'd191; + zifi_en <= 1'b0; + end + ER: begin + dataout <= 1'b1; + dout <= er; + end + DR: begin + dataout <= 1'b1; + dout <= fifo_rx_q; + fifo_rx_rdreq <= zifi_en; + end + endcase + end + + if (iowr_s) begin + casez (a) + CR: begin + casez (din) + 8'b000000??: begin + fifo_rx_sclr <= din[0]; + fifo_tx_sclr <= din[1]; + er <= 8'h00; + end + 8'b000001??: begin + er <= 8'h00; + end + 8'b11110???: begin + er <= 8'h00; + end + 8'b11111111: begin + er <= 8'h01; + end + default: begin + er <= 8'hFF; + end + endcase + end + DR: begin + fifo_tx_data <= din; + fifo_tx_wrreq <= zifi_en; + end + endcase + end + + end + + + // workarround to fix random hang of zifi.spg (a2cfe54), which is always doing 191-bytes-inir (see fifo_inir function) + reg [19:0] rx_busy_cnt = 0; + reg rx_busy = 0; + always @(posedge clk) begin + if (fifo_rx_wrreq) + rx_busy_cnt <= 1'd1; + else if (rx_busy_cnt) + rx_busy_cnt <= rx_busy_cnt + 1'd1; + rx_busy <= (fifo_rx_wrreq || rx_busy_cnt) && fifo_rx_usedw < 191; + end + + + wire [7:0] fifo_rx_data; + wire fifo_rx_wrreq; + reg fifo_rx_rdreq; + reg fifo_rx_sclr; + wire [7:0] fifo_rx_q; + wire [12:0] fifo_rx_usedw; + scfifo + #( + .lpm_width(8), + .lpm_widthu(13), + .lpm_numwords(8192), + .lpm_showahead("ON"), + .overflow_checking("ON"), + .underflow_checking("ON"), + .add_ram_output_register("OFF") + ) + fifo_rx + ( + .clock(clk), + .data(fifo_rx_data), + .wrreq(fifo_rx_wrreq), + .rdreq(fifo_rx_rdreq), + .sclr(rst | fifo_rx_sclr), + .q(fifo_rx_q), + .usedw(fifo_rx_usedw) + ); + + + reg [7:0] fifo_tx_data; + reg fifo_tx_wrreq; + wire fifo_tx_rdreq; + reg fifo_tx_sclr; + wire [7:0] fifo_tx_q; + wire [7:0] fifo_tx_usedw; + wire [7:0] fifo_tx_freew = 8'h255 - fifo_tx_usedw; + wire fifo_tx_empty; + reg fifo_tx_empty_r; + always @(posedge clk) + fifo_tx_empty_r <= fifo_tx_empty; + scfifo + #( + .lpm_width(8), + .lpm_widthu(8), + .lpm_numwords(256), + .lpm_showahead("ON"), + .overflow_checking("ON"), + .underflow_checking("ON"), + .add_ram_output_register("OFF") + ) + fifo_tx + ( + .clock(clk), + .data(fifo_tx_data), + .wrreq(fifo_tx_wrreq), + .rdreq(fifo_tx_rdreq), + .sclr(rst | fifo_tx_sclr), + .q(fifo_tx_q), + .usedw(fifo_tx_usedw), + .empty(fifo_tx_empty) + ); + + + uart_rx #(.CLKS_PER_BIT(28_000_000/115200)) uart_rx + ( + .i_Clock(clk), + .i_Rx_Serial(rx), + .o_Rx_DV(fifo_rx_wrreq), + .o_Rx_Byte(fifo_rx_data) + ); + + + wire tx_busy; + reg tx_busy_r; + always @(posedge clk) + tx_busy_r <= tx_busy; + assign fifo_tx_rdreq = tx_busy && !tx_busy_r; + uart_tx #(.CLKS_PER_BIT(28_000_000/115200)) uart_tx + ( + .i_Clock(clk), + .i_Tx_DV(!fifo_tx_empty_r), + .i_Tx_Byte(fifo_tx_q), + .o_Tx_Active(tx_busy), + .o_Tx_Serial(tx), + .o_Tx_Done() + ); + + +endmodule diff --git a/rtl/tsconf.v b/rtl/tsconf.v index 031f724..4f89df5 100644 --- a/rtl/tsconf.v +++ b/rtl/tsconf.v @@ -47,6 +47,8 @@ module tsconf input TAPE_IN, output TAPE_OUT, output MIDI_OUT, + input UART_RX, + output UART_TX, // Configuration bits input CFG_OUT0, @@ -979,6 +981,26 @@ module tsconf ); + // ZiFi + wire [7:0] zifi_do; + wire zifi_dataout; + + zifi zifi + ( + .clk(fclk), + .rst(rst), + .din(d), + .dout(zifi_do), + .dataout(zifi_dataout), + .a(a), + .iord(iord), + .iord_s(iord_s), + .iowr_s(iowr_s), + .rx(UART_RX), + .tx(UART_TX) + ); + + // Soundrive wire [7:0] covox_a; wire [7:0] covox_b; @@ -1126,6 +1148,7 @@ module tsconf (~mreq_n && ~rd_n) ? dout_ram : // SDRAM (gs_sel && ~rd_n) ? gs_do_bus : // General Sound (ts_enable && ~rd_n) ? ts_do : // TurboSound + (zifi_dataout && ~rd_n) ? zifi_do : // ZiFi (ena_ports) ? dout_ports : (intack) ? im2vect : 8'b11111111;