mirror of
https://github.com/MiSTer-devel/MacPlus_MiSTer.git
synced 2024-11-27 02:49:32 +00:00
working serial port for console and ppp (#6)
This commit is contained in:
parent
19b22bdc6d
commit
4cd1724296
@ -13,7 +13,7 @@ set_global_assignment -name PARTITION_NETLIST_TYPE SOURCE -section_id Top
|
|||||||
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
|
set_global_assignment -name PARTITION_FITTER_PRESERVATION_LEVEL PLACEMENT_AND_ROUTING -section_id Top
|
||||||
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
|
set_global_assignment -name PARTITION_COLOR 16764057 -section_id Top
|
||||||
|
|
||||||
set_global_assignment -name LAST_QUARTUS_VERSION "17.0.2 Standard Edition"
|
set_global_assignment -name LAST_QUARTUS_VERSION "17.0.2 Lite Edition"
|
||||||
|
|
||||||
set_global_assignment -name GENERATE_RBF_FILE ON
|
set_global_assignment -name GENERATE_RBF_FILE ON
|
||||||
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
|
set_global_assignment -name PROJECT_OUTPUT_DIRECTORY output_files
|
||||||
|
49
MacPlus.sv
49
MacPlus.sv
@ -175,7 +175,7 @@ module emu
|
|||||||
|
|
||||||
assign ADC_BUS = 'Z;
|
assign ADC_BUS = 'Z;
|
||||||
assign USER_OUT = '1;
|
assign USER_OUT = '1;
|
||||||
assign {UART_RTS, UART_TXD, UART_DTR} = 0;
|
|
||||||
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = 0;
|
assign {DDRAM_CLK, DDRAM_BURSTCNT, DDRAM_ADDR, DDRAM_DIN, DDRAM_BE, DDRAM_RD, DDRAM_WE} = 0;
|
||||||
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
|
assign {SD_SCK, SD_MOSI, SD_CS} = 'Z;
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ video_freak video_freak
|
|||||||
|
|
||||||
`include "build_id.v"
|
`include "build_id.v"
|
||||||
localparam CONF_STR = {
|
localparam CONF_STR = {
|
||||||
"MACPLUS;;",
|
"MACPLUS;UART115200;",
|
||||||
"-;",
|
"-;",
|
||||||
"F1,DSK,Mount Pri Floppy;",
|
"F1,DSK,Mount Pri Floppy;",
|
||||||
"F2,DSK,Mount Sec Floppy;",
|
"F2,DSK,Mount Sec Floppy;",
|
||||||
@ -216,6 +216,8 @@ localparam CONF_STR = {
|
|||||||
"ODE,CPU,68000,68010,68020;",
|
"ODE,CPU,68000,68010,68020;",
|
||||||
"O4,Memory,1MB,4MB;",
|
"O4,Memory,1MB,4MB;",
|
||||||
"-;",
|
"-;",
|
||||||
|
//"OA,Serial,Off,On;",
|
||||||
|
//"-;",
|
||||||
"R0,Reset & Apply CPU+Memory;",
|
"R0,Reset & Apply CPU+Memory;",
|
||||||
"V,v",`BUILD_DATE
|
"V,v",`BUILD_DATE
|
||||||
};
|
};
|
||||||
@ -358,11 +360,44 @@ assign AUDIO_MIX = 0;
|
|||||||
|
|
||||||
|
|
||||||
// set the real-world inputs to sane defaults
|
// set the real-world inputs to sane defaults
|
||||||
localparam serialIn = 1'b0,
|
localparam configROMSize = 1'b1; // 128K ROM
|
||||||
configROMSize = 1'b1; // 128K ROM
|
|
||||||
|
|
||||||
wire [1:0] configRAMSize = status_mem?2'b11:2'b10; // 1MB/4MB
|
wire [1:0] configRAMSize = status_mem?2'b11:2'b10; // 1MB/4MB
|
||||||
|
|
||||||
|
//
|
||||||
|
// Serial Ports
|
||||||
|
//
|
||||||
|
wire serialOut;
|
||||||
|
wire serialIn;
|
||||||
|
wire serialCTS;
|
||||||
|
wire serialRTS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
assign serialIn = ~status[10] ? 0 : UART_RXD;
|
||||||
|
assign UART_TXD = serialOut;
|
||||||
|
assign serialCTS = UART_CTS;
|
||||||
|
assign UART_RTS = serialRTS;
|
||||||
|
assign UART_DTR = UART_DSR;
|
||||||
|
*/
|
||||||
|
|
||||||
|
//assign serialIn = ~status[10] ? 0 : UART_RXD;
|
||||||
|
assign serialIn = UART_RXD;
|
||||||
|
assign UART_TXD = serialOut;
|
||||||
|
//assign UART_RTS = UART_CTS;
|
||||||
|
assign UART_RTS = serialRTS ;
|
||||||
|
assign UART_DTR = UART_DSR;
|
||||||
|
|
||||||
|
//assign {UART_RTS, UART_TXD, UART_DTR} = 0;
|
||||||
|
/*
|
||||||
|
input UART_CTS,
|
||||||
|
output UART_RTS,
|
||||||
|
input UART_RXD,
|
||||||
|
output UART_TXD,
|
||||||
|
output UART_DTR,
|
||||||
|
input UART_DSR,
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// interconnects
|
// interconnects
|
||||||
// CPU
|
// CPU
|
||||||
wire clk8, _cpuReset, _cpuReset_o, _cpuUDS, _cpuLDS, _cpuRW, _cpuAS;
|
wire clk8, _cpuReset, _cpuReset_o, _cpuUDS, _cpuLDS, _cpuRW, _cpuAS;
|
||||||
@ -618,7 +653,13 @@ dataController_top dc0
|
|||||||
.ps2_key(ps2_key),
|
.ps2_key(ps2_key),
|
||||||
.capslock(capslock),
|
.capslock(capslock),
|
||||||
.ps2_mouse(ps2_mouse),
|
.ps2_mouse(ps2_mouse),
|
||||||
|
// serial uart
|
||||||
.serialIn(serialIn),
|
.serialIn(serialIn),
|
||||||
|
.serialOut(serialOut),
|
||||||
|
.serialCTS(serialCTS),
|
||||||
|
.serialRTS(serialRTS),
|
||||||
|
|
||||||
|
// rtc unix ticks
|
||||||
.timestamp(TIMESTAMP),
|
.timestamp(TIMESTAMP),
|
||||||
|
|
||||||
// video
|
// video
|
||||||
|
@ -8,6 +8,8 @@ set_global_assignment -name VERILOG_FILE rtl/floppy.v
|
|||||||
set_global_assignment -name SYSTEMVERILOG_FILE rtl/ps2_kbd.sv
|
set_global_assignment -name SYSTEMVERILOG_FILE rtl/ps2_kbd.sv
|
||||||
set_global_assignment -name VERILOG_FILE rtl/ps2_mouse.v
|
set_global_assignment -name VERILOG_FILE rtl/ps2_mouse.v
|
||||||
set_global_assignment -name VERILOG_FILE rtl/scc.v
|
set_global_assignment -name VERILOG_FILE rtl/scc.v
|
||||||
|
set_global_assignment -name VERILOG_FILE rtl/uart/txuart.v
|
||||||
|
set_global_assignment -name VERILOG_FILE rtl/uart/rxuart.v
|
||||||
set_global_assignment -name VERILOG_FILE rtl/iwm.v
|
set_global_assignment -name VERILOG_FILE rtl/iwm.v
|
||||||
set_global_assignment -name VHDL_FILE rtl/via6522.vhd
|
set_global_assignment -name VHDL_FILE rtl/via6522.vhd
|
||||||
set_global_assignment -name VERILOG_FILE rtl/addrDecoder.v
|
set_global_assignment -name VERILOG_FILE rtl/addrDecoder.v
|
||||||
|
@ -47,6 +47,8 @@ module dataController_top(
|
|||||||
// serial:
|
// serial:
|
||||||
input serialIn,
|
input serialIn,
|
||||||
output serialOut,
|
output serialOut,
|
||||||
|
input serialCTS,
|
||||||
|
output serialRTS,
|
||||||
|
|
||||||
// RTC
|
// RTC
|
||||||
input [32:0] timestamp,
|
input [32:0] timestamp,
|
||||||
@ -148,8 +150,6 @@ module dataController_top(
|
|||||||
!_sccIrq?3'b101:
|
!_sccIrq?3'b101:
|
||||||
3'b111;
|
3'b111;
|
||||||
|
|
||||||
// Serial port
|
|
||||||
assign serialOut = 0;
|
|
||||||
|
|
||||||
reg [15:0] cpu_data;
|
reg [15:0] cpu_data;
|
||||||
always @(posedge clk32) if (cpuBusControl && memoryLatch) cpu_data <= memoryDataIn;
|
always @(posedge clk32) if (cpuBusControl && memoryLatch) cpu_data <= memoryDataIn;
|
||||||
@ -409,7 +409,12 @@ module dataController_top(
|
|||||||
._irq(_sccIrq),
|
._irq(_sccIrq),
|
||||||
.dcd_a(mouseX1),
|
.dcd_a(mouseX1),
|
||||||
.dcd_b(mouseY1),
|
.dcd_b(mouseY1),
|
||||||
.wreq(sccWReq));
|
.wreq(sccWReq),
|
||||||
|
.txd(serialOut),
|
||||||
|
.rxd(serialIn),
|
||||||
|
.cts(serialCTS),
|
||||||
|
.rts(serialRTS)
|
||||||
|
);
|
||||||
|
|
||||||
// Video
|
// Video
|
||||||
videoShifter vs(
|
videoShifter vs(
|
||||||
|
@ -22,7 +22,7 @@ module ps2_kbd
|
|||||||
|
|
||||||
reg [8:0] keymac;
|
reg [8:0] keymac;
|
||||||
reg key_pending;
|
reg key_pending;
|
||||||
reg [21:0] pacetimer;
|
reg [19:0] pacetimer;
|
||||||
reg inquiry_active;
|
reg inquiry_active;
|
||||||
reg cmd_inquiry;
|
reg cmd_inquiry;
|
||||||
reg cmd_instant;
|
reg cmd_instant;
|
||||||
@ -68,8 +68,8 @@ always@(posedge clk or posedge reset) begin
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
wire tick_long = pacetimer == 22'h3fffff;
|
wire tick_long = pacetimer == 20'hfffff;
|
||||||
wire tick_short = pacetimer == 22'h000fff;
|
wire tick_short = pacetimer == 20'h00fff;
|
||||||
|
|
||||||
/* Delay inquiry responses to after tick_short */
|
/* Delay inquiry responses to after tick_short */
|
||||||
always@(posedge clk or posedge reset) begin
|
always@(posedge clk or posedge reset) begin
|
||||||
|
391
rtl/scc.v
391
rtl/scc.v
@ -62,7 +62,8 @@ module scc
|
|||||||
wire reset;
|
wire reset;
|
||||||
|
|
||||||
/* Data registers */
|
/* Data registers */
|
||||||
reg [7:0] data_a = 0;
|
// reg [7:0] data_a = 0;
|
||||||
|
wire[7:0] data_a ;
|
||||||
reg [7:0] data_b = 0;
|
reg [7:0] data_b = 0;
|
||||||
|
|
||||||
/* Read registers */
|
/* Read registers */
|
||||||
@ -84,6 +85,10 @@ module scc
|
|||||||
reg [7:0] wr1_a;
|
reg [7:0] wr1_a;
|
||||||
reg [7:0] wr1_b;
|
reg [7:0] wr1_b;
|
||||||
reg [7:0] wr2;
|
reg [7:0] wr2;
|
||||||
|
reg [7:0] wr3_a; /* synthesis keep */
|
||||||
|
reg [7:0] wr3_b;
|
||||||
|
reg [7:0] wr4_a;
|
||||||
|
reg [7:0] wr4_b;
|
||||||
reg [7:0] wr5_a;
|
reg [7:0] wr5_a;
|
||||||
reg [7:0] wr5_b;
|
reg [7:0] wr5_b;
|
||||||
reg [7:0] wr6_a;
|
reg [7:0] wr6_a;
|
||||||
@ -105,8 +110,10 @@ module scc
|
|||||||
/* Status latches */
|
/* Status latches */
|
||||||
reg latch_open_a;
|
reg latch_open_a;
|
||||||
reg latch_open_b;
|
reg latch_open_b;
|
||||||
|
reg cts_latch_a;
|
||||||
reg dcd_latch_a;
|
reg dcd_latch_a;
|
||||||
reg dcd_latch_b;
|
reg dcd_latch_b;
|
||||||
|
wire cts_ip_a;
|
||||||
wire dcd_ip_a;
|
wire dcd_ip_a;
|
||||||
wire dcd_ip_b;
|
wire dcd_ip_b;
|
||||||
wire do_latch_a;
|
wire do_latch_a;
|
||||||
@ -125,6 +132,10 @@ module scc
|
|||||||
reg ex_irq_ip_b;
|
reg ex_irq_ip_b;
|
||||||
wire [2:0] rr2_vec_stat;
|
wire [2:0] rr2_vec_stat;
|
||||||
|
|
||||||
|
reg [7:0] tx_data_a;
|
||||||
|
reg wr8_wr_a;
|
||||||
|
reg wr8_wr_b;
|
||||||
|
|
||||||
/* Register/Data access helpers */
|
/* Register/Data access helpers */
|
||||||
assign wreg_a = cs & we & (~rs[1]) & rs[0];
|
assign wreg_a = cs & we & (~rs[1]) & rs[0];
|
||||||
assign wreg_b = cs & we & (~rs[1]) & ~rs[0];
|
assign wreg_b = cs & we & (~rs[1]) & ~rs[0];
|
||||||
@ -136,11 +147,28 @@ module scc
|
|||||||
/* Register index is set by a write to WR0 and reset
|
/* Register index is set by a write to WR0 and reset
|
||||||
* after any subsequent write. We ignore the side
|
* after any subsequent write. We ignore the side
|
||||||
*/
|
*/
|
||||||
always@(posedge clk or posedge reset) begin
|
reg wr_data_a;
|
||||||
|
reg wr_data_b;
|
||||||
|
|
||||||
|
reg rx_wr_a_latch;
|
||||||
|
reg rx_first_a=1;
|
||||||
|
always@(posedge clk /*or posedge reset*/) begin
|
||||||
|
|
||||||
|
if (rx_wr_a) begin
|
||||||
|
rx_wr_a_latch<=1;
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
wr_data_a<=0;
|
||||||
|
wr_data_b<=0;
|
||||||
if (reset) begin
|
if (reset) begin
|
||||||
rindex_latch <= 0;
|
rindex_latch <= 0;
|
||||||
data_a <= 0;
|
//data_a <= 0;
|
||||||
|
tx_data_a<=0;
|
||||||
data_b <= 0;
|
data_b <= 0;
|
||||||
|
rx_wr_a_latch<=0;
|
||||||
|
wr_data_a<=0;
|
||||||
|
rx_first_a<=1;
|
||||||
end else if (cen && cs) begin
|
end else if (cen && cs) begin
|
||||||
if (!rs[1]) begin
|
if (!rs[1]) begin
|
||||||
/* Default, reset index */
|
/* Default, reset index */
|
||||||
@ -153,11 +181,32 @@ module scc
|
|||||||
|
|
||||||
/* Add point high */
|
/* Add point high */
|
||||||
rindex_latch[3] <= (wdata[5:3] == 3'b001);
|
rindex_latch[3] <= (wdata[5:3] == 3'b001);
|
||||||
|
/* enable int on next rx char */
|
||||||
|
if (wdata[5:3] == 3'b100)
|
||||||
|
rx_first_a<=1;
|
||||||
end
|
end
|
||||||
end else begin
|
end else begin
|
||||||
if (we) begin
|
if (we) begin
|
||||||
if (rs[0]) data_a <= wdata;
|
if (rs[0]) begin
|
||||||
else data_b <= wdata;
|
//data_a <= wdata;
|
||||||
|
tx_data_a <= wdata;
|
||||||
|
wr_data_a<=1;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
data_b <= wdata;
|
||||||
|
wr_data_b<=1;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
// clear the read register?
|
||||||
|
if (rs[0]) begin
|
||||||
|
rx_wr_a_latch<=0;
|
||||||
|
rx_first_a<=0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -207,6 +256,37 @@ module scc
|
|||||||
wr2 <= wdata;
|
wr2 <= wdata;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
/* WR3
|
||||||
|
* Reset: unchanged
|
||||||
|
*/
|
||||||
|
always@(posedge clk or posedge reset_hw) begin
|
||||||
|
if (reset_hw)
|
||||||
|
wr3_a <= 0;
|
||||||
|
else if (cen && wreg_a && rindex == 3)
|
||||||
|
wr3_a <= wdata;
|
||||||
|
end
|
||||||
|
always@(posedge clk or posedge reset_hw) begin
|
||||||
|
if (reset_hw)
|
||||||
|
wr3_b <= 0;
|
||||||
|
else if (cen && wreg_b && rindex == 3)
|
||||||
|
wr3_b <= wdata;
|
||||||
|
end
|
||||||
|
/* WR4
|
||||||
|
* Reset: unchanged
|
||||||
|
*/
|
||||||
|
always@(posedge clk or posedge reset_hw) begin
|
||||||
|
if (reset_hw)
|
||||||
|
wr4_a <= 0;
|
||||||
|
else if (cen && wreg_a && rindex == 4)
|
||||||
|
wr4_a <= wdata;
|
||||||
|
end
|
||||||
|
always@(posedge clk or posedge reset_hw) begin
|
||||||
|
if (reset_hw)
|
||||||
|
wr4_b <= 0;
|
||||||
|
else if (cen && wreg_b && rindex == 4)
|
||||||
|
wr4_b <= wdata;
|
||||||
|
end
|
||||||
|
|
||||||
/* WR5
|
/* WR5
|
||||||
* Reset: Bits 7,4,3,2,1 to 0
|
* Reset: Bits 7,4,3,2,1 to 0
|
||||||
*/
|
*/
|
||||||
@ -231,6 +311,39 @@ module scc
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
/* WR8 : write data to serial port -- a or b?
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
always@(posedge clk or posedge reset_hw) begin
|
||||||
|
if (reset_hw) begin
|
||||||
|
wr8_a <= 0;
|
||||||
|
wr8_wr_a <= 1'b0;
|
||||||
|
end
|
||||||
|
else if (cen && (rs[1] & we ) && rindex == 8) begin
|
||||||
|
wr8_wr_a <= 1'b1;
|
||||||
|
wr8_a <= wdata;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
wr8_wr_a <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
always@(posedge clk or posedge reset_hw) begin
|
||||||
|
if (reset_hw) begin
|
||||||
|
wr8_b <= 0;
|
||||||
|
wr8_wr_b <= 1'b0;
|
||||||
|
end
|
||||||
|
else if (cen && (wreg_b ) && rindex == 8)
|
||||||
|
begin
|
||||||
|
wr8_wr_b <= 1'b1;
|
||||||
|
wr8_b <= wdata;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
wr8_wr_b <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
/* WR9. Special: top bits are reset, handled separately, bottom
|
/* WR9. Special: top bits are reset, handled separately, bottom
|
||||||
* bits are only reset by a hw reset
|
* bits are only reset by a hw reset
|
||||||
*/
|
*/
|
||||||
@ -378,12 +491,13 @@ module scc
|
|||||||
/* RR0 */
|
/* RR0 */
|
||||||
assign rr0_a = { 1'b0, /* Break */
|
assign rr0_a = { 1'b0, /* Break */
|
||||||
1'b1, /* Tx Underrun/EOM */
|
1'b1, /* Tx Underrun/EOM */
|
||||||
1'b0, /* CTS */
|
wr15_a[5] ? cts_latch_a : cts_a, /* CTS */
|
||||||
1'b0, /* Sync/Hunt */
|
1'b0, /* Sync/Hunt */
|
||||||
wr15_a[3] ? dcd_latch_a : dcd_a, /* DCD */
|
wr15_a[3] ? dcd_latch_a : dcd_a, /* DCD */
|
||||||
1'b1, /* Tx Empty */
|
//1'b1, /*TX EMPTY */
|
||||||
|
~tx_busy_a, /* Tx Empty */
|
||||||
1'b0, /* Zero Count */
|
1'b0, /* Zero Count */
|
||||||
1'b0 /* Rx Available */
|
rx_wr_a_latch /* Rx Available */
|
||||||
};
|
};
|
||||||
assign rr0_b = { 1'b0, /* Break */
|
assign rr0_b = { 1'b0, /* Break */
|
||||||
1'b1, /* Tx Underrun/EOM */
|
1'b1, /* Tx Underrun/EOM */
|
||||||
@ -397,13 +511,13 @@ module scc
|
|||||||
|
|
||||||
/* RR1 */
|
/* RR1 */
|
||||||
assign rr1_a = { 1'b0, /* End of frame */
|
assign rr1_a = { 1'b0, /* End of frame */
|
||||||
1'b0, /* CRC/Framing error */
|
1'b0,//frame_err_a, /* CRC/Framing error */
|
||||||
1'b0, /* Rx Overrun error */
|
1'b0, /* Rx Overrun error */
|
||||||
1'b0, /* Parity error */
|
1'b0,//parity_err_a, /* Parity error */
|
||||||
1'b0, /* Residue code 0 */
|
1'b0, /* Residue code 0 */
|
||||||
1'b1, /* Residue code 1 */
|
1'b1, /* Residue code 1 */
|
||||||
1'b1, /* Residue code 2 */
|
1'b1, /* Residue code 2 */
|
||||||
1'b1 /* All sent */
|
~tx_busy_a /* All sent */
|
||||||
};
|
};
|
||||||
|
|
||||||
assign rr1_b = { 1'b0, /* End of frame */
|
assign rr1_b = { 1'b0, /* End of frame */
|
||||||
@ -483,15 +597,110 @@ module scc
|
|||||||
*
|
*
|
||||||
* Need to add latches. Tx irq is latched when buffer goes from full->empty,
|
* Need to add latches. Tx irq is latched when buffer goes from full->empty,
|
||||||
* it's not a permanent state. For now keep it clear. Will have to fix that.
|
* it's not a permanent state. For now keep it clear. Will have to fix that.
|
||||||
|
* TODO: AJS - look at tx and interrupt logic
|
||||||
*/
|
*/
|
||||||
assign rx_irq_pend_a = 0;
|
|
||||||
assign tx_irq_pend_a = 0 /*& wr1_a[1]*/; /* Tx always empty for now */
|
/*
|
||||||
assign ex_irq_pend_a = ex_irq_ip_a;
|
The TxIP is reset either by writing data to the transmit buffer or by issuing the Reset Tx Int command in WR0
|
||||||
|
*/
|
||||||
|
|
||||||
|
reg tx_fin_pre;
|
||||||
|
reg tx_ip;
|
||||||
|
reg tx_mip;
|
||||||
|
|
||||||
|
/*
|
||||||
|
reg tx_ie;
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (reset_a|reset_hw|reset)
|
||||||
|
tx_ie<=0;
|
||||||
|
else if (wreg_a & (rindex == 1) )
|
||||||
|
tx_ie<=wdata[1];
|
||||||
|
end
|
||||||
|
*/
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
if (reset) begin
|
||||||
|
tx_ip<=0;
|
||||||
|
tx_mip<=0;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
tx_fin_pre<=tx_busy_a;
|
||||||
|
|
||||||
|
if (wr5_a[3] & wr1_a[1] & tx_busy_a & ~tx_fin_pre) begin
|
||||||
|
tx_ip<=~tx_mip;
|
||||||
|
tx_mip<=0;
|
||||||
|
end
|
||||||
|
if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b111)) begin
|
||||||
|
tx_ip<=0;
|
||||||
|
end
|
||||||
|
if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b101)) begin
|
||||||
|
// If CIP=1, inhibit generation of next TX interrupt
|
||||||
|
// Actually, "Reset TxInt pend." clears current interrupt
|
||||||
|
tx_mip<= ~tx_ip;
|
||||||
|
tx_ip<=0;
|
||||||
|
end
|
||||||
|
if (wr5_a[3]==0)begin
|
||||||
|
tx_mip<=0;
|
||||||
|
tx_ip<=0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
reg tx_busy_a_r;
|
||||||
|
reg tx_latch_a;
|
||||||
|
always @(posedge clk) begin
|
||||||
|
tx_busy_a_r <= tx_busy_a;
|
||||||
|
// when we transition from empty to full, we create an interrupt
|
||||||
|
if (reset | reset_hw | reset_a)
|
||||||
|
tx_latch_a<=0;
|
||||||
|
else if (tx_busy_a_r ==1 && tx_busy_a==0)
|
||||||
|
tx_latch_a<=1;
|
||||||
|
// cleared when we write again
|
||||||
|
else if (wr_data_a)
|
||||||
|
tx_latch_a<=0;
|
||||||
|
// or when we set the reset in wr0
|
||||||
|
else if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b010))
|
||||||
|
tx_latch_a<=0;
|
||||||
|
//else if (wreg_a & (rindex == 0) & (wdata[5:3] == 3'b111)) // clear highest under service?
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wire wreq_n;
|
||||||
|
//assign rx_irq_pend_a = rx_wr_a_latch & ( (wr1_a[3] && ~wr1_a[4])|| (~wr1_a[3] && wr1_a[4])) & wr3_a[0]; /* figure out the interrupt on / off */
|
||||||
|
//assign rx_irq_pend_a = rx_wr_a_latch & ( (wr1_a[3] & ~wr1_a[4])| (~wr1_a[3] & wr1_a[4])) & wr3_a[0]; /* figure out the interrupt on / off */
|
||||||
|
|
||||||
|
/* figure out the interrupt on / off */
|
||||||
|
/* rx enable: wr3_a[0] */
|
||||||
|
/* wr1_a 4 3
|
||||||
|
0 0 = rx int disable
|
||||||
|
0 1 = rx int on first char or special
|
||||||
|
1 0 = rx int on all rx chars or special
|
||||||
|
1 1 = rx int on special cond only
|
||||||
|
*/
|
||||||
|
// rx enable char waiting 01,10 only first char
|
||||||
|
assign rx_irq_pend_a = wr3_a[0] & rx_wr_a_latch & (wr1_a[3] ^ wr1_a[4]) & ((wr1_a[3] & rx_first_a )|(wr1_a[4]));
|
||||||
|
|
||||||
|
// assign tx_irq_pend_a = 0;
|
||||||
|
// assign tx_irq_pend_a = tx_busy_a & wr1_a[1];
|
||||||
|
|
||||||
|
assign tx_irq_pend_a = tx_ip;
|
||||||
|
//assign tx_irq_pend_a = wr1_a[1]; /* Tx always empty for now */
|
||||||
|
|
||||||
|
wire cts_interrupt = wr1_a[0] && wr15_a[5] || (tx_busy_a_r ==1 && tx_busy_a==0) || (tx_busy_a_r ==0 && tx_busy_a==1);/* if cts changes */
|
||||||
|
|
||||||
|
assign ex_irq_pend_a = ex_irq_ip_a ;
|
||||||
assign rx_irq_pend_b = 0;
|
assign rx_irq_pend_b = 0;
|
||||||
assign tx_irq_pend_b = 0 /*& wr1_b[1]*/; /* Tx always empty for now */
|
assign tx_irq_pend_b = 0 /*& wr1_b[1]*/; /* Tx always empty for now */
|
||||||
assign ex_irq_pend_b = ex_irq_ip_b;
|
assign ex_irq_pend_b = ex_irq_ip_b;
|
||||||
|
|
||||||
assign _irq = ~(wr9[3] & (rx_irq_pend_a |
|
assign _irq = ~(wr9[3] & (rx_irq_pend_a |
|
||||||
|
|
||||||
|
|
||||||
rx_irq_pend_b |
|
rx_irq_pend_b |
|
||||||
tx_irq_pend_a |
|
tx_irq_pend_a |
|
||||||
tx_irq_pend_b |
|
tx_irq_pend_b |
|
||||||
@ -514,12 +723,13 @@ module scc
|
|||||||
* corresponding interrupt is enabled in WR15
|
* corresponding interrupt is enabled in WR15
|
||||||
*/
|
*/
|
||||||
assign dcd_ip_a = (dcd_a != dcd_latch_a) & wr15_a[3];
|
assign dcd_ip_a = (dcd_a != dcd_latch_a) & wr15_a[3];
|
||||||
|
assign cts_ip_a = (cts_a != cts_latch_a) & wr15_a[5];
|
||||||
assign dcd_ip_b = (dcd_b != dcd_latch_b) & wr15_b[3];
|
assign dcd_ip_b = (dcd_b != dcd_latch_b) & wr15_b[3];
|
||||||
|
|
||||||
/* Latches close when an enabled IP bit is set and latches
|
/* Latches close when an enabled IP bit is set and latches
|
||||||
* are currently open
|
* are currently open
|
||||||
*/
|
*/
|
||||||
assign do_latch_a = latch_open_a & (dcd_ip_a /* | cts... */);
|
assign do_latch_a = latch_open_a & (dcd_ip_a | cts_ip_a /* | cts... */);
|
||||||
assign do_latch_b = latch_open_b & (dcd_ip_b /* | cts... */);
|
assign do_latch_b = latch_open_b & (dcd_ip_b /* | cts... */);
|
||||||
|
|
||||||
/* "Master" interrupt, set when latch close & WR1[0] is set */
|
/* "Master" interrupt, set when latch close & WR1[0] is set */
|
||||||
@ -570,10 +780,12 @@ module scc
|
|||||||
always@(posedge clk or posedge reset) begin
|
always@(posedge clk or posedge reset) begin
|
||||||
if (reset) begin
|
if (reset) begin
|
||||||
dcd_latch_a <= 0;
|
dcd_latch_a <= 0;
|
||||||
|
cts_latch_a <= 0;
|
||||||
/* cts ... */
|
/* cts ... */
|
||||||
end else if(cep) begin
|
end else if(cep) begin
|
||||||
if (do_latch_a)
|
if (do_latch_a)
|
||||||
dcd_latch_a <= dcd_a;
|
dcd_latch_a <= dcd_a;
|
||||||
|
cts_latch_a <= cts_a;
|
||||||
/* cts ... */
|
/* cts ... */
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -588,9 +800,150 @@ module scc
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
/* NYI */
|
|
||||||
assign txd = 1;
|
|
||||||
assign rts = 1;
|
|
||||||
|
|
||||||
assign wreq = 1;
|
|
||||||
|
/* NYI */
|
||||||
|
// assign txd = 1;
|
||||||
|
// assign rts = 1;
|
||||||
|
|
||||||
|
/* UART */
|
||||||
|
|
||||||
|
//wr_3_a
|
||||||
|
//wr_3_b
|
||||||
|
// bit
|
||||||
|
wire parity_ena_a= wr4_a[0];
|
||||||
|
wire parity_even_a= wr4_a[1];
|
||||||
|
reg [1:0] stop_bits_a= 2'b00;
|
||||||
|
reg [1:0] bit_per_char_a = 2'b00;
|
||||||
|
/*
|
||||||
|
76543210
|
||||||
|
data>>2 & 3
|
||||||
|
wr4_a[3:2]
|
||||||
|
case(wr4_a[3:2])
|
||||||
|
2'b00:
|
||||||
|
// sync mode enable
|
||||||
|
2'b01:
|
||||||
|
// 1 stop bit
|
||||||
|
stop_bits_a <= 2'b0;
|
||||||
|
2'b10:
|
||||||
|
// 1.5 stop bit
|
||||||
|
stop_bits_a <= 2'b0;
|
||||||
|
2'b11:
|
||||||
|
// 2 stop bit
|
||||||
|
stop_bits_a <= 2'b1;
|
||||||
|
default:
|
||||||
|
stop_bits_a <= 2'b0;
|
||||||
|
endcase
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
76543210
|
||||||
|
^__ 76
|
||||||
|
wr_3_a[7:6] -- bits per char
|
||||||
|
|
||||||
|
case (wr_3_a[7:6]})
|
||||||
|
2'b00: // 5
|
||||||
|
bit_per_char_a <= 2'b11;
|
||||||
|
2'b01: // 7
|
||||||
|
bit_per_char_a <= 2'b01;
|
||||||
|
2'b10: // 6
|
||||||
|
bit_per_char_a <= 2'b10;
|
||||||
|
2'b11: // 8
|
||||||
|
bit_per_char_a <= 2'b00;
|
||||||
|
endcase
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
300 -- 62.668800 / = 208896
|
||||||
|
600 -- 62.668800 / = 104448
|
||||||
|
1200-- 62.668800 / = 69632
|
||||||
|
2400 -- 62.668800 / 2400 = 26112
|
||||||
|
4800 -- 62.668800 / 4800 = 13056
|
||||||
|
9600 -- 62.668800 / 9600 = 6528
|
||||||
|
1440 -- 62.668800 / 14400 = 4352
|
||||||
|
19200 -- 62.668800 / 19200= 3264
|
||||||
|
38400 -- 62.668800 / 28800 = 2176
|
||||||
|
38400 -- 62.668800 / 38400 = 1632
|
||||||
|
57600 -- 62.668800 / 57600 = 1088
|
||||||
|
115200 -- 62.668800 / 115200 = 544
|
||||||
|
230400 -- 62.668800 / 230400 = 272
|
||||||
|
|
||||||
|
|
||||||
|
32.5 / 115200 =
|
||||||
|
|
||||||
|
*/
|
||||||
|
// case the baud rate based on wr12_a and 13_a
|
||||||
|
// wr_12_a -- contains the baud rate lower byte
|
||||||
|
// wr_13_a -- contains the baud rate high byte
|
||||||
|
/*
|
||||||
|
always @(posedge clk) begin
|
||||||
|
case ({wr13_a,wr12_a})
|
||||||
|
16'd380: // 300 baud
|
||||||
|
baud_divid_speed_a <= 24'd108333;
|
||||||
|
16'd94: // 1200 baud
|
||||||
|
baud_divid_speed_a <= 24'd27083;
|
||||||
|
16'd46: // 2400 baud
|
||||||
|
baud_divid_speed_a <= 24'd13542;
|
||||||
|
16'd22: // 4800 baud
|
||||||
|
baud_divid_speed_a <= 24'd6770;
|
||||||
|
16'd10: // 9600 baud
|
||||||
|
baud_divid_speed_a <= 24'd3385;
|
||||||
|
16'd6: // 14400 baud
|
||||||
|
baud_divid_speed_a <= 24'd2257;
|
||||||
|
16'd4: // 19200 baud
|
||||||
|
baud_divid_speed_a <= 24'd1693;
|
||||||
|
16'd2: // 28800 baud
|
||||||
|
baud_divid_speed_a <= 24'd1128;
|
||||||
|
16'd1: // 38400 baud
|
||||||
|
baud_divid_speed_a <= 24'd846;
|
||||||
|
16'd0: // 57600 baud
|
||||||
|
baud_divid_speed_a <= 24'd564;
|
||||||
|
default:
|
||||||
|
baud_divid_speed_a <= 24'd282;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//reg [23:0] baud_divid_speed_a = 24'd1088;
|
||||||
|
//reg [23:0] baud_divid_speed_a = 24'd544;
|
||||||
|
reg [23:0] baud_divid_speed_a = 24'd282;
|
||||||
|
//reg [23:0] baud_divid_speed_a = 24'd564;
|
||||||
|
wire tx_busy_a;
|
||||||
|
wire rx_wr_a;
|
||||||
|
wire [30:0] uart_setup_rx_a = { 1'b0, bit_per_char_a, 1'b0, parity_ena_a, 1'b0, parity_even_a, baud_divid_speed_a } ;
|
||||||
|
wire [30:0] uart_setup_tx_a = { 1'b0, bit_per_char_a, 1'b0, parity_ena_a, 1'b0, parity_even_a, baud_divid_speed_a } ;
|
||||||
|
//wire [30:0] uart_setup_rx_a = { 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, baud_divid_speed_a } ;
|
||||||
|
//wire [30:0] uart_setup_tx_a = { 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, 1'b0, baud_divid_speed_a } ;
|
||||||
|
rxuart rxuart_a (
|
||||||
|
.i_clk(clk),
|
||||||
|
.i_reset(reset_a|reset_hw),
|
||||||
|
.i_setup(uart_setup_rx_a),
|
||||||
|
.i_uart_rx(rxd),
|
||||||
|
.o_wr(rx_wr_a), // TODO -- check on this flag
|
||||||
|
.o_data(data_a), // TODO we need to save this off only if wreq is set, and mux it into data_a in the right spot
|
||||||
|
.o_break(break_a),
|
||||||
|
.o_parity_err(parity_err_a),
|
||||||
|
.o_frame_err(frame_err_a),
|
||||||
|
.o_ck_uart()
|
||||||
|
);
|
||||||
|
txuart txuart_a
|
||||||
|
(
|
||||||
|
.i_clk(clk),
|
||||||
|
.i_reset(reset_a|reset_hw),
|
||||||
|
.i_setup(uart_setup_tx_a),
|
||||||
|
.i_break(1'b0),
|
||||||
|
.i_wr(wr_data_a), // TODO -- we need to send data when we get the register command i guess???
|
||||||
|
.i_data(tx_data_a),
|
||||||
|
//.i_cts_n(~cts),
|
||||||
|
.i_cts_n(1'b0),
|
||||||
|
.o_uart_tx(txd),
|
||||||
|
.o_busy(tx_busy_a)); // TODO -- do we need this busy line?? probably
|
||||||
|
|
||||||
|
wire cts_a = ~tx_busy_a;
|
||||||
|
|
||||||
|
// RTS and CTS are active low
|
||||||
|
assign rts = rx_wr_a_latch;
|
||||||
|
assign wreq=1;
|
||||||
endmodule
|
endmodule
|
||||||
|
467
rtl/uart/rxuart.v
Normal file
467
rtl/uart/rxuart.v
Normal file
@ -0,0 +1,467 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Filename: rxuart.v
|
||||||
|
//
|
||||||
|
// Project: wbuart32, a full featured UART with simulator
|
||||||
|
//
|
||||||
|
// Purpose: Receive and decode inputs from a single UART line.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// To interface with this module, connect it to your system clock,
|
||||||
|
// pass it the 32 bit setup register (defined below) and the UART
|
||||||
|
// input. When data becomes available, the o_wr line will be asserted
|
||||||
|
// for one clock cycle. On parity or frame errors, the o_parity_err
|
||||||
|
// or o_frame_err lines will be asserted. Likewise, on a break
|
||||||
|
// condition, o_break will be asserted. These lines are self clearing.
|
||||||
|
//
|
||||||
|
// There is a synchronous reset line, logic high.
|
||||||
|
//
|
||||||
|
// Now for the setup register. The register is 32 bits, so that this
|
||||||
|
// UART may be set up over a 32-bit bus.
|
||||||
|
//
|
||||||
|
// i_setup[30] True if we are not using hardware flow control. This bit
|
||||||
|
// is ignored within this module, as any receive hardware flow
|
||||||
|
// control will need to be implemented elsewhere.
|
||||||
|
//
|
||||||
|
// i_setup[29:28] Indicates the number of data bits per word. This will
|
||||||
|
// either be 2'b00 for an 8-bit word, 2'b01 for a 7-bit word, 2'b10
|
||||||
|
// for a six bit word, or 2'b11 for a five bit word.
|
||||||
|
//
|
||||||
|
// i_setup[27] Indicates whether or not to use one or two stop bits.
|
||||||
|
// Set this to one to expect two stop bits, zero for one.
|
||||||
|
//
|
||||||
|
// i_setup[26] Indicates whether or not a parity bit exists. Set this
|
||||||
|
// to 1'b1 to include parity.
|
||||||
|
//
|
||||||
|
// i_setup[25] Indicates whether or not the parity bit is fixed. Set
|
||||||
|
// to 1'b1 to include a fixed bit of parity, 1'b0 to allow the
|
||||||
|
// parity to be set based upon data. (Both assume the parity
|
||||||
|
// enable value is set.)
|
||||||
|
//
|
||||||
|
// i_setup[24] This bit is ignored if parity is not used. Otherwise,
|
||||||
|
// in the case of a fixed parity bit, this bit indicates whether
|
||||||
|
// mark (1'b1) or space (1'b0) parity is used. Likewise if the
|
||||||
|
// parity is not fixed, a 1'b1 selects even parity, and 1'b0
|
||||||
|
// selects odd.
|
||||||
|
//
|
||||||
|
// i_setup[23:0] Indicates the speed of the UART in terms of clocks.
|
||||||
|
// So, for example, if you have a 200 MHz clock and wish to
|
||||||
|
// run your UART at 9600 baud, you would take 200 MHz and divide
|
||||||
|
// by 9600 to set this value to 24'd20834. Likewise if you wished
|
||||||
|
// to run this serial port at 115200 baud from a 200 MHz clock,
|
||||||
|
// you would set the value to 24'd1736
|
||||||
|
//
|
||||||
|
// Thus, to set the UART for the common setting of an 8-bit word,
|
||||||
|
// one stop bit, no parity, and 115200 baud over a 200 MHz clock, you
|
||||||
|
// would want to set the setup value to:
|
||||||
|
//
|
||||||
|
// 32'h0006c8 // For 115,200 baud, 8 bit, no parity
|
||||||
|
// 32'h005161 // For 9600 baud, 8 bit, no parity
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Creator: Dan Gisselquist, Ph.D.
|
||||||
|
// Gisselquist Technology, LLC
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Copyright (C) 2015-2019, Gisselquist Technology, LLC
|
||||||
|
//
|
||||||
|
// This program is free software (firmware): 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 program is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY 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. (It's in the $(ROOT)/doc directory. Run make with no
|
||||||
|
// target there if the PDF file isn't present.) If not, see
|
||||||
|
// <http://www.gnu.org/licenses/> for a copy.
|
||||||
|
//
|
||||||
|
// License: GPL, v3, as defined and found on www.gnu.org,
|
||||||
|
// http://www.gnu.org/licenses/gpl.html
|
||||||
|
//
|
||||||
|
//
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
//
|
||||||
|
`default_nettype none
|
||||||
|
//
|
||||||
|
// States: (@ baud counter == 0)
|
||||||
|
// 0 First bit arrives
|
||||||
|
// ..7 Bits arrive
|
||||||
|
// 8 Stop bit (x1)
|
||||||
|
// 9 Stop bit (x2)
|
||||||
|
// c break condition
|
||||||
|
// d Waiting for the channel to go high
|
||||||
|
// e Waiting for the reset to complete
|
||||||
|
// f Idle state
|
||||||
|
`define RXU_BIT_ZERO 4'h0
|
||||||
|
`define RXU_BIT_ONE 4'h1
|
||||||
|
`define RXU_BIT_TWO 4'h2
|
||||||
|
`define RXU_BIT_THREE 4'h3
|
||||||
|
`define RXU_BIT_FOUR 4'h4
|
||||||
|
`define RXU_BIT_FIVE 4'h5
|
||||||
|
`define RXU_BIT_SIX 4'h6
|
||||||
|
`define RXU_BIT_SEVEN 4'h7
|
||||||
|
`define RXU_PARITY 4'h8
|
||||||
|
`define RXU_STOP 4'h9
|
||||||
|
`define RXU_SECOND_STOP 4'ha
|
||||||
|
// Unused 4'hb
|
||||||
|
// Unused 4'hc
|
||||||
|
`define RXU_BREAK 4'hd
|
||||||
|
`define RXU_RESET_IDLE 4'he
|
||||||
|
`define RXU_IDLE 4'hf
|
||||||
|
|
||||||
|
module rxuart(i_clk, i_reset, i_setup, i_uart_rx, o_wr, o_data, o_break,
|
||||||
|
o_parity_err, o_frame_err, o_ck_uart);
|
||||||
|
parameter [30:0] INITIAL_SETUP = 31'd868;
|
||||||
|
// 8 data bits, no parity, (at least 1) stop bit
|
||||||
|
input wire i_clk, i_reset;
|
||||||
|
/* verilator lint_off UNUSED */
|
||||||
|
input wire [30:0] i_setup;
|
||||||
|
/* verilator lint_on UNUSED */
|
||||||
|
input wire i_uart_rx;
|
||||||
|
output reg o_wr;
|
||||||
|
output reg [7:0] o_data;
|
||||||
|
output reg o_break;
|
||||||
|
output reg o_parity_err, o_frame_err;
|
||||||
|
output wire o_ck_uart;
|
||||||
|
|
||||||
|
|
||||||
|
wire [27:0] clocks_per_baud, break_condition, half_baud;
|
||||||
|
wire [1:0] data_bits;
|
||||||
|
wire use_parity, parity_even, dblstop, fixd_parity;
|
||||||
|
reg [29:0] r_setup;
|
||||||
|
reg [3:0] state;
|
||||||
|
|
||||||
|
assign clocks_per_baud = { 4'h0, r_setup[23:0] };
|
||||||
|
// assign hw_flow_control = !r_setup[30];
|
||||||
|
assign data_bits = r_setup[29:28];
|
||||||
|
assign dblstop = r_setup[27];
|
||||||
|
assign use_parity = r_setup[26];
|
||||||
|
assign fixd_parity = r_setup[25];
|
||||||
|
assign parity_even = r_setup[24];
|
||||||
|
assign break_condition = { r_setup[23:0], 4'h0 };
|
||||||
|
assign half_baud = { 5'h00, r_setup[23:1] }-28'h1;
|
||||||
|
reg [27:0] baud_counter;
|
||||||
|
reg zero_baud_counter;
|
||||||
|
|
||||||
|
|
||||||
|
// Since this is an asynchronous receiver, we need to register our
|
||||||
|
// input a couple of clocks over to avoid any problems with
|
||||||
|
// metastability. We do that here, and then ignore all but the
|
||||||
|
// ck_uart wire.
|
||||||
|
reg q_uart, qq_uart, ck_uart;
|
||||||
|
initial q_uart = 1'b0;
|
||||||
|
initial qq_uart = 1'b0;
|
||||||
|
initial ck_uart = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
begin
|
||||||
|
q_uart <= i_uart_rx;
|
||||||
|
qq_uart <= q_uart;
|
||||||
|
ck_uart <= qq_uart;
|
||||||
|
end
|
||||||
|
|
||||||
|
// In case anyone else wants this clocked, stabilized value, we
|
||||||
|
// offer it on our output.
|
||||||
|
assign o_ck_uart = ck_uart;
|
||||||
|
|
||||||
|
// Keep track of the number of clocks since the last change.
|
||||||
|
//
|
||||||
|
// This is used to determine if we are in either a break or an idle
|
||||||
|
// condition, as discussed further below.
|
||||||
|
reg [27:0] chg_counter;
|
||||||
|
initial chg_counter = 28'h00;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if (i_reset)
|
||||||
|
chg_counter <= 28'h00;
|
||||||
|
else if (qq_uart != ck_uart)
|
||||||
|
chg_counter <= 28'h00;
|
||||||
|
else if (chg_counter < break_condition)
|
||||||
|
chg_counter <= chg_counter + 1;
|
||||||
|
|
||||||
|
// Are we in a break condition?
|
||||||
|
//
|
||||||
|
// A break condition exists if the line is held low for longer than
|
||||||
|
// a data word. Hence, we keep track of when the last change occurred.
|
||||||
|
// If it was more than break_condition clocks ago, and the current input
|
||||||
|
// value is a 0, then we're in a break--and nothing can be read until
|
||||||
|
// the line idles again.
|
||||||
|
initial o_break = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
o_break <= ((chg_counter >= break_condition)&&(~ck_uart))? 1'b1:1'b0;
|
||||||
|
|
||||||
|
// Are we between characters?
|
||||||
|
//
|
||||||
|
// The opposite of a break condition is where the line is held high
|
||||||
|
// for more clocks than would be in a character. When this happens,
|
||||||
|
// we know we have synchronization--otherwise, we might be sampling
|
||||||
|
// from within a data word.
|
||||||
|
//
|
||||||
|
// This logic is used later to hold the RXUART in a reset condition
|
||||||
|
// until we know we are between data words. At that point, we should
|
||||||
|
// be able to hold on to our synchronization.
|
||||||
|
reg line_synch;
|
||||||
|
initial line_synch = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
line_synch <= ((chg_counter >= break_condition)&&(ck_uart));
|
||||||
|
|
||||||
|
// Are we in the middle of a baud iterval? Specifically, are we
|
||||||
|
// in the middle of a start bit? Set this to high if so. We'll use
|
||||||
|
// this within our state machine to transition out of the IDLE
|
||||||
|
// state.
|
||||||
|
reg half_baud_time;
|
||||||
|
initial half_baud_time = 0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud);
|
||||||
|
|
||||||
|
|
||||||
|
// Allow our controlling processor to change our setup at any time
|
||||||
|
// outside of receiving/processing a character.
|
||||||
|
initial r_setup = INITIAL_SETUP[29:0];
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if (state >= `RXU_RESET_IDLE)
|
||||||
|
r_setup <= i_setup[29:0];
|
||||||
|
|
||||||
|
|
||||||
|
// Our monster state machine. YIKES!
|
||||||
|
//
|
||||||
|
// Yeah, this may be more complicated than it needs to be. The basic
|
||||||
|
// progression is:
|
||||||
|
// RESET -> RESET_IDLE -> (when line is idle) -> IDLE
|
||||||
|
// IDLE -> bit 0 -> bit 1 -> bit_{ndatabits} ->
|
||||||
|
// (optional) PARITY -> STOP -> (optional) SECOND_STOP
|
||||||
|
// -> IDLE
|
||||||
|
// ANY -> (on break) BREAK -> IDLE
|
||||||
|
//
|
||||||
|
// There are 16 states, although all are not used. These are listed
|
||||||
|
// at the top of this file.
|
||||||
|
//
|
||||||
|
// Logic inputs (12): (I've tried to minimize this number)
|
||||||
|
// state (4)
|
||||||
|
// i_reset
|
||||||
|
// line_synch
|
||||||
|
// o_break
|
||||||
|
// ckuart
|
||||||
|
// half_baud_time
|
||||||
|
// zero_baud_counter
|
||||||
|
// use_parity
|
||||||
|
// dblstop
|
||||||
|
// Logic outputs (4):
|
||||||
|
// state
|
||||||
|
//
|
||||||
|
initial state = `RXU_RESET_IDLE;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
begin
|
||||||
|
if (i_reset)
|
||||||
|
state <= `RXU_RESET_IDLE;
|
||||||
|
else if (state == `RXU_RESET_IDLE)
|
||||||
|
begin
|
||||||
|
if (line_synch)
|
||||||
|
// Goto idle state from a reset
|
||||||
|
state <= `RXU_IDLE;
|
||||||
|
else // Otherwise, stay in this condition 'til reset
|
||||||
|
state <= `RXU_RESET_IDLE;
|
||||||
|
end else if (o_break)
|
||||||
|
begin // We are in a break condition
|
||||||
|
state <= `RXU_BREAK;
|
||||||
|
end else if (state == `RXU_BREAK)
|
||||||
|
begin // Goto idle state following return ck_uart going high
|
||||||
|
if (ck_uart)
|
||||||
|
state <= `RXU_IDLE;
|
||||||
|
else
|
||||||
|
state <= `RXU_BREAK;
|
||||||
|
end else if (state == `RXU_IDLE)
|
||||||
|
begin // Idle state, independent of baud counter
|
||||||
|
if ((~ck_uart)&&(half_baud_time))
|
||||||
|
begin
|
||||||
|
// We are in the center of a valid start bit
|
||||||
|
case (data_bits)
|
||||||
|
2'b00: state <= `RXU_BIT_ZERO;
|
||||||
|
2'b01: state <= `RXU_BIT_ONE;
|
||||||
|
2'b10: state <= `RXU_BIT_TWO;
|
||||||
|
2'b11: state <= `RXU_BIT_THREE;
|
||||||
|
endcase
|
||||||
|
end else // Otherwise, just stay here in idle
|
||||||
|
state <= `RXU_IDLE;
|
||||||
|
end else if (zero_baud_counter)
|
||||||
|
begin
|
||||||
|
if (state < `RXU_BIT_SEVEN)
|
||||||
|
// Data arrives least significant bit first.
|
||||||
|
// By the time this is clocked in, it's what
|
||||||
|
// you'll have.
|
||||||
|
state <= state + 1;
|
||||||
|
else if (state == `RXU_BIT_SEVEN)
|
||||||
|
state <= (use_parity) ? `RXU_PARITY:`RXU_STOP;
|
||||||
|
else if (state == `RXU_PARITY)
|
||||||
|
state <= `RXU_STOP;
|
||||||
|
else if (state == `RXU_STOP)
|
||||||
|
begin // Stop (or parity) bit(s)
|
||||||
|
if (~ck_uart) // On frame error, wait 4 ch idle
|
||||||
|
state <= `RXU_RESET_IDLE;
|
||||||
|
else if (dblstop)
|
||||||
|
state <= `RXU_SECOND_STOP;
|
||||||
|
else
|
||||||
|
state <= `RXU_IDLE;
|
||||||
|
end else // state must equal RX_SECOND_STOP
|
||||||
|
begin
|
||||||
|
if (~ck_uart) // On frame error, wait 4 ch idle
|
||||||
|
state <= `RXU_RESET_IDLE;
|
||||||
|
else
|
||||||
|
state <= `RXU_IDLE;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Data bit capture logic.
|
||||||
|
//
|
||||||
|
// This is drastically simplified from the state machine above, based
|
||||||
|
// upon: 1) it doesn't matter what it is until the end of a captured
|
||||||
|
// byte, and 2) the data register will flush itself of any invalid
|
||||||
|
// data in all other cases. Hence, let's keep it real simple.
|
||||||
|
// The only trick, though, is that if we have parity, then the data
|
||||||
|
// register needs to be held through that state without getting
|
||||||
|
// updated.
|
||||||
|
reg [7:0] data_reg;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if ((zero_baud_counter)&&(state != `RXU_PARITY))
|
||||||
|
data_reg <= { ck_uart, data_reg[7:1] };
|
||||||
|
|
||||||
|
// Parity calculation logic
|
||||||
|
//
|
||||||
|
// As with the data capture logic, all that must be known about this
|
||||||
|
// bit is that it is the exclusive-OR of all bits prior. The first
|
||||||
|
// of those will follow idle, so we set ourselves to zero on idle.
|
||||||
|
// Then, as we walk through the states of a bit, all will adjust this
|
||||||
|
// value up until the parity bit, where the value will be read. Setting
|
||||||
|
// it then or after will be irrelevant, so ... this should be good
|
||||||
|
// and simplified. Note--we don't need to adjust this on reset either,
|
||||||
|
// since the reset state will lead to the idle state where we'll be
|
||||||
|
// reset before any transmission takes place.
|
||||||
|
reg calc_parity;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if (state == `RXU_IDLE)
|
||||||
|
calc_parity <= 0;
|
||||||
|
else if (zero_baud_counter)
|
||||||
|
calc_parity <= calc_parity ^ ck_uart;
|
||||||
|
|
||||||
|
// Parity error logic
|
||||||
|
//
|
||||||
|
// Set during the parity bit interval, read during the last stop bit
|
||||||
|
// interval, cleared on BREAK, RESET_IDLE, or IDLE states.
|
||||||
|
initial o_parity_err = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if ((zero_baud_counter)&&(state == `RXU_PARITY))
|
||||||
|
begin
|
||||||
|
if (fixd_parity)
|
||||||
|
// Fixed parity bit--independent of any dat
|
||||||
|
// value.
|
||||||
|
o_parity_err <= (ck_uart ^ parity_even);
|
||||||
|
else if (parity_even)
|
||||||
|
// Parity even: The XOR of all bits including
|
||||||
|
// the parity bit must be zero.
|
||||||
|
o_parity_err <= (calc_parity != ck_uart);
|
||||||
|
else
|
||||||
|
// Parity odd: the parity bit must equal the
|
||||||
|
// XOR of all the data bits.
|
||||||
|
o_parity_err <= (calc_parity == ck_uart);
|
||||||
|
end else if (state >= `RXU_BREAK)
|
||||||
|
o_parity_err <= 1'b0;
|
||||||
|
|
||||||
|
// Frame error determination
|
||||||
|
//
|
||||||
|
// For the purpose of this controller, a frame error is defined as a
|
||||||
|
// stop bit (or second stop bit, if so enabled) not being high midway
|
||||||
|
// through the stop baud interval. The frame error value is
|
||||||
|
// immediately read, so we can clear it under all other circumstances.
|
||||||
|
// Specifically, we want it clear in RXU_BREAK, RXU_RESET_IDLE, and
|
||||||
|
// most importantly in RXU_IDLE.
|
||||||
|
initial o_frame_err = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if ((zero_baud_counter)&&((state == `RXU_STOP)
|
||||||
|
||(state == `RXU_SECOND_STOP)))
|
||||||
|
o_frame_err <= (o_frame_err)||(~ck_uart);
|
||||||
|
else if ((zero_baud_counter)||(state >= `RXU_BREAK))
|
||||||
|
o_frame_err <= 1'b0;
|
||||||
|
|
||||||
|
// Our data bit logic doesn't need nearly the complexity of all that
|
||||||
|
// work above. Indeed, we only need to know if we are at the end of
|
||||||
|
// a stop bit, in which case we copy the data_reg into our output
|
||||||
|
// data register, o_data.
|
||||||
|
//
|
||||||
|
// We would also set o_wr to be true when this is the case, but ... we
|
||||||
|
// won't know if there is a frame error on the second stop bit for
|
||||||
|
// another baud interval yet. So, instead, we set up the logic so that
|
||||||
|
// we know on the next zero baud counter that we can write out. That's
|
||||||
|
// the purpose of pre_wr.
|
||||||
|
initial o_data = 8'h00;
|
||||||
|
reg pre_wr;
|
||||||
|
initial pre_wr = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if (i_reset)
|
||||||
|
begin
|
||||||
|
pre_wr <= 1'b0;
|
||||||
|
o_data <= 8'h00;
|
||||||
|
end else if ((zero_baud_counter)&&(state == `RXU_STOP))
|
||||||
|
begin
|
||||||
|
pre_wr <= 1'b1;
|
||||||
|
case (data_bits)
|
||||||
|
2'b00: o_data <= data_reg;
|
||||||
|
2'b01: o_data <= { 1'b0, data_reg[7:1] };
|
||||||
|
2'b10: o_data <= { 2'b0, data_reg[7:2] };
|
||||||
|
2'b11: o_data <= { 3'b0, data_reg[7:3] };
|
||||||
|
endcase
|
||||||
|
end else if ((zero_baud_counter)||(state == `RXU_IDLE))
|
||||||
|
pre_wr <= 1'b0;
|
||||||
|
|
||||||
|
// Create an output strobe, true for one clock only, once we know
|
||||||
|
// all we need to know. o_data will be set on the last baud interval,
|
||||||
|
// o_parity_err on the last parity baud interval (if it existed,
|
||||||
|
// cleared otherwise, so ... we should be good to go here.)
|
||||||
|
initial o_wr = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if ((zero_baud_counter)||(state == `RXU_IDLE))
|
||||||
|
o_wr <= (pre_wr)&&(!i_reset);
|
||||||
|
else
|
||||||
|
o_wr <= 1'b0;
|
||||||
|
|
||||||
|
// The baud counter
|
||||||
|
//
|
||||||
|
// This is used as a "clock divider" if you will, but the clock needs
|
||||||
|
// to be reset before any byte can be decoded. In all other respects,
|
||||||
|
// we set ourselves up for clocks_per_baud counts between baud
|
||||||
|
// intervals.
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if (i_reset)
|
||||||
|
baud_counter <= clocks_per_baud-28'h01;
|
||||||
|
else if (zero_baud_counter)
|
||||||
|
baud_counter <= clocks_per_baud-28'h01;
|
||||||
|
else case(state)
|
||||||
|
`RXU_RESET_IDLE:baud_counter <= clocks_per_baud-28'h01;
|
||||||
|
`RXU_BREAK: baud_counter <= clocks_per_baud-28'h01;
|
||||||
|
`RXU_IDLE: baud_counter <= clocks_per_baud-28'h01;
|
||||||
|
default: baud_counter <= baud_counter-28'h01;
|
||||||
|
endcase
|
||||||
|
|
||||||
|
// zero_baud_counter
|
||||||
|
//
|
||||||
|
// Rather than testing whether or not (baud_counter == 0) within our
|
||||||
|
// (already too complicated) state transition tables, we use
|
||||||
|
// zero_baud_counter to pre-charge that test on the clock
|
||||||
|
// before--cleaning up some otherwise difficult timing dependencies.
|
||||||
|
initial zero_baud_counter = 1'b0;
|
||||||
|
always @(posedge i_clk)
|
||||||
|
if (state == `RXU_IDLE)
|
||||||
|
zero_baud_counter <= 1'b0;
|
||||||
|
else
|
||||||
|
zero_baud_counter <= (baud_counter == 28'h01);
|
||||||
|
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
|
1138
rtl/uart/txuart.v
Normal file
1138
rtl/uart/txuart.v
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user