950 lines
23 KiB
Verilog
950 lines
23 KiB
Verilog
`timescale 1ns / 100ps
|
|
|
|
/*
|
|
* Zilog 8530 SCC module for minimigmac.
|
|
*
|
|
* Located on high data bus, but writes are done at odd addresses as
|
|
* LDS is used as WR signals or something like that on a Mac Plus.
|
|
*
|
|
* We don't care here and just ignore which side was used.
|
|
*
|
|
* NOTE: We don't implement the 85C30 or ESCC additions such as WR7'
|
|
* for now, it's all very simplified
|
|
*/
|
|
|
|
module scc
|
|
(
|
|
input clk,
|
|
input cep,
|
|
input cen,
|
|
|
|
input reset_hw,
|
|
|
|
/* Bus interface. 2-bit address, to be wired
|
|
* appropriately upstream (to A1..A2).
|
|
*/
|
|
input cs,
|
|
input we,
|
|
input [1:0] rs, /* [1] = data(1)/ctl [0] = a_side(1)/b_side */
|
|
input [7:0] wdata,
|
|
output [7:0] rdata,
|
|
output _irq,
|
|
|
|
/* A single serial port on Minimig */
|
|
input rxd,
|
|
output txd,
|
|
input cts, /* normally wired to device DTR output
|
|
* on Mac cables. That same line is also
|
|
* connected to the TRxC input of the SCC
|
|
* to do fast clocking but we don't do that
|
|
* here
|
|
*/
|
|
output rts, /* on a real mac this activates line
|
|
* drivers when low */
|
|
|
|
/* DCD for both ports are hijacked by mouse interface */
|
|
input dcd_a, /* We don't synchronize those inputs */
|
|
input dcd_b,
|
|
|
|
/* Write request */
|
|
output wreq
|
|
);
|
|
|
|
/* Register access is semi-insane */
|
|
reg [3:0] rindex;
|
|
reg [3:0] rindex_latch;
|
|
wire wreg_a;
|
|
wire wreg_b;
|
|
|
|
/* Resets via WR9, one clk pulses */
|
|
wire reset_a;
|
|
wire reset_b;
|
|
wire reset;
|
|
|
|
/* Data registers */
|
|
// reg [7:0] data_a = 0;
|
|
wire[7:0] data_a ;
|
|
reg [7:0] data_b = 0;
|
|
|
|
/* Read registers */
|
|
wire [7:0] rr0_a;
|
|
wire [7:0] rr0_b;
|
|
wire [7:0] rr1_a;
|
|
wire [7:0] rr1_b;
|
|
wire [7:0] rr2_b;
|
|
wire [7:0] rr3_a;
|
|
wire [7:0] rr10_a;
|
|
wire [7:0] rr10_b;
|
|
wire [7:0] rr15_a;
|
|
wire [7:0] rr15_b;
|
|
|
|
/* Write registers. Only some are implemented,
|
|
* some result in actions on write and don't
|
|
* store anything
|
|
*/
|
|
reg [7:0] wr1_a;
|
|
reg [7:0] wr1_b;
|
|
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_b;
|
|
reg [7:0] wr6_a;
|
|
reg [7:0] wr6_b;
|
|
reg [7:0] wr8_a;
|
|
reg [7:0] wr8_b;
|
|
reg [5:0] wr9;
|
|
reg [7:0] wr10_a;
|
|
reg [7:0] wr10_b;
|
|
reg [7:0] wr12_a;
|
|
reg [7:0] wr12_b;
|
|
reg [7:0] wr13_a;
|
|
reg [7:0] wr13_b;
|
|
reg [7:0] wr14_a;
|
|
reg [7:0] wr14_b;
|
|
reg [7:0] wr15_a;
|
|
reg [7:0] wr15_b;
|
|
|
|
/* Status latches */
|
|
reg latch_open_a;
|
|
reg latch_open_b;
|
|
reg cts_latch_a;
|
|
reg dcd_latch_a;
|
|
reg dcd_latch_b;
|
|
wire cts_ip_a;
|
|
wire dcd_ip_a;
|
|
wire dcd_ip_b;
|
|
wire do_latch_a;
|
|
wire do_latch_b;
|
|
wire do_extreset_a;
|
|
wire do_extreset_b;
|
|
|
|
/* IRQ stuff */
|
|
wire rx_irq_pend_a;
|
|
wire rx_irq_pend_b;
|
|
wire tx_irq_pend_a;
|
|
wire tx_irq_pend_b;
|
|
wire ex_irq_pend_a;
|
|
wire ex_irq_pend_b;
|
|
reg ex_irq_ip_a;
|
|
reg ex_irq_ip_b;
|
|
wire [2:0] rr2_vec_stat;
|
|
|
|
reg [7:0] tx_data_a;
|
|
reg wr8_wr_a;
|
|
reg wr8_wr_b;
|
|
|
|
/* Register/Data access helpers */
|
|
assign wreg_a = cs & we & (~rs[1]) & rs[0];
|
|
assign wreg_b = cs & we & (~rs[1]) & ~rs[0];
|
|
|
|
// make sure rindex changes after the cpu cycle has ended so
|
|
// read data is still stable while cpu advances
|
|
always@(posedge clk) if(~cs) rindex <= rindex_latch;
|
|
|
|
/* Register index is set by a write to WR0 and reset
|
|
* after any subsequent write. We ignore the side
|
|
*/
|
|
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
|
|
rindex_latch <= 0;
|
|
//data_a <= 0;
|
|
tx_data_a<=0;
|
|
data_b <= 0;
|
|
rx_wr_a_latch<=0;
|
|
wr_data_a<=0;
|
|
rx_first_a<=1;
|
|
end else if (cen && cs) begin
|
|
if (!rs[1]) begin
|
|
/* Default, reset index */
|
|
rindex_latch <= 0;
|
|
|
|
/* Write to WR0 */
|
|
if (we && rindex == 0) begin
|
|
/* Get low index bits */
|
|
rindex_latch[2:0] <= wdata[2:0];
|
|
|
|
/* Add point high */
|
|
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 else begin
|
|
if (we) begin
|
|
if (rs[0]) begin
|
|
//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
|
|
|
|
/* Reset logic (write to WR9 cmd)
|
|
*
|
|
* Note about resets: Some bits are documented as unchanged/undefined on
|
|
* HW reset by the doc. We apply this to channel and soft resets, however
|
|
* we _do_ reset every bit on an external HW reset in this implementation
|
|
* to make the FPGA & synthesis tools happy.
|
|
*/
|
|
assign reset = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b11)) | reset_hw;
|
|
assign reset_a = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b10)) | reset;
|
|
assign reset_b = ((wreg_a | wreg_b) & (rindex == 9) & (wdata[7:6] == 2'b01)) | reset;
|
|
|
|
/* WR1
|
|
* Reset: bit 5 and 2 unchanged */
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr1_a <= 0;
|
|
else if(cen) begin
|
|
if (reset_a)
|
|
wr1_a <= { 2'b00, wr1_a[5], 2'b00, wr1_a[2], 2'b00 };
|
|
else if (wreg_a && rindex == 1)
|
|
wr1_a <= wdata;
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr1_b <= 0;
|
|
else if(cen) begin
|
|
if (reset_b)
|
|
wr1_b <= { 2'b00, wr1_b[5], 2'b00, wr1_b[2], 2'b00 };
|
|
else if (wreg_b && rindex == 1)
|
|
wr1_b <= wdata;
|
|
end
|
|
end
|
|
|
|
/* WR2
|
|
* Reset: unchanged
|
|
*/
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr2 <= 0;
|
|
else if (cen && (wreg_a || wreg_b) && rindex == 2)
|
|
wr2 <= wdata;
|
|
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
|
|
* Reset: Bits 7,4,3,2,1 to 0
|
|
*/
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr5_a <= 0;
|
|
else if(cen) begin
|
|
if (reset_a)
|
|
wr5_a <= { 1'b0, wr5_a[6:5], 4'b0000, wr5_a[0] };
|
|
else if (wreg_a && rindex == 5)
|
|
wr5_a <= wdata;
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr5_b <= 0;
|
|
else if(cen) begin
|
|
if (reset_b)
|
|
wr5_b <= { 1'b0, wr5_b[6:5], 4'b0000, wr5_b[0] };
|
|
else if (wreg_b && rindex == 5)
|
|
wr5_b <= wdata;
|
|
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
|
|
* bits are only reset by a hw reset
|
|
*/
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr9 <= 0;
|
|
else if (cen && (wreg_a || wreg_b) && rindex == 9)
|
|
wr9 <= wdata[5:0];
|
|
end
|
|
|
|
/* WR10
|
|
* Reset: all 0, except chanel reset retains 6 and 5
|
|
*/
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
wr10_a <= 0;
|
|
else if(cen) begin
|
|
if (reset_a)
|
|
wr10_a <= { 1'b0, wr10_a[6:5], 5'b00000 };
|
|
else if (wreg_a && rindex == 10)
|
|
wr10_a <= wdata;
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
wr10_b <= 0;
|
|
else if(cen) begin
|
|
if (reset_b)
|
|
wr10_b <= { 1'b0, wr10_b[6:5], 5'b00000 };
|
|
else if (wreg_b && rindex == 10)
|
|
wr10_b <= wdata;
|
|
end
|
|
end
|
|
|
|
/* WR12
|
|
* Reset: Unchanged
|
|
*/
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr12_a <= 0;
|
|
else if (cen && wreg_a && rindex == 12)
|
|
wr12_a <= wdata;
|
|
end
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr12_b <= 0;
|
|
else if (cen && wreg_b && rindex == 12)
|
|
wr12_b <= wdata;
|
|
end
|
|
|
|
/* WR13
|
|
* Reset: Unchanged
|
|
*/
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr13_a <= 0;
|
|
else if (cen && wreg_a && rindex == 13)
|
|
wr13_a <= wdata;
|
|
end
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr13_b <= 0;
|
|
else if (cen && wreg_b && rindex == 13)
|
|
wr13_b <= wdata;
|
|
end
|
|
|
|
/* WR14
|
|
* Reset: Full reset maintains top 2 bits,
|
|
* Chan reset also maitains bottom 2 bits, bit 4 also
|
|
* reset to a different value
|
|
*/
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr14_a <= 0;
|
|
else if(cen) begin
|
|
if (reset)
|
|
wr14_a <= { wr14_a[7:6], 6'b110000 };
|
|
else if (reset_a)
|
|
wr14_a <= { wr14_a[7:6], 4'b1000, wr14_a[1:0] };
|
|
else if (wreg_a && rindex == 14)
|
|
wr14_a <= wdata;
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset_hw) begin
|
|
if (reset_hw)
|
|
wr14_b <= 0;
|
|
else if(cen) begin
|
|
if (reset)
|
|
wr14_b <= { wr14_b[7:6], 6'b110000 };
|
|
else if (reset_b)
|
|
wr14_b <= { wr14_b[7:6], 4'b1000, wr14_b[1:0] };
|
|
else if (wreg_b && rindex == 14)
|
|
wr14_b <= wdata;
|
|
end
|
|
end
|
|
|
|
/* WR15 */
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset) begin
|
|
wr15_a <= 8'b11111000;
|
|
wr15_b <= 8'b11111000;
|
|
end else if (cen && rindex == 15) begin
|
|
if(wreg_a) wr15_a <= wdata;
|
|
if(wreg_b) wr15_b <= wdata;
|
|
end
|
|
end
|
|
|
|
/* Read data mux */
|
|
assign rdata = rs[1] && rs[0] ? data_a :
|
|
rs[1] ? data_b :
|
|
rindex == 0 && rs[0] ? rr0_a :
|
|
rindex == 0 ? rr0_b :
|
|
rindex == 1 && rs[0] ? rr1_a :
|
|
rindex == 1 ? rr1_b :
|
|
rindex == 2 && rs[0] ? wr2 :
|
|
rindex == 2 ? rr2_b :
|
|
rindex == 3 && rs[0] ? rr3_a :
|
|
rindex == 3 ? 8'h00 :
|
|
rindex == 4 && rs[0] ? rr0_a :
|
|
rindex == 4 ? rr0_b :
|
|
rindex == 5 && rs[0] ? rr1_a :
|
|
rindex == 5 ? rr1_b :
|
|
rindex == 6 && rs[0] ? wr2 :
|
|
rindex == 6 ? rr2_b :
|
|
rindex == 7 && rs[0] ? rr3_a :
|
|
rindex == 7 ? 8'h00 :
|
|
|
|
rindex == 8 && rs[0] ? data_a :
|
|
rindex == 8 ? data_b :
|
|
rindex == 9 && rs[0] ? wr13_a :
|
|
rindex == 9 ? wr13_b :
|
|
rindex == 10 && rs[0] ? rr10_a :
|
|
rindex == 10 ? rr10_b :
|
|
rindex == 11 && rs[0] ? rr15_a :
|
|
rindex == 11 ? rr15_b :
|
|
rindex == 12 && rs[0] ? wr12_a :
|
|
rindex == 12 ? wr12_b :
|
|
rindex == 13 && rs[0] ? wr13_a :
|
|
rindex == 13 ? wr13_b :
|
|
rindex == 14 && rs[0] ? rr10_a :
|
|
rindex == 14 ? rr10_b :
|
|
rindex == 15 && rs[0] ? rr15_a :
|
|
rindex == 15 ? rr15_b : 8'hff;
|
|
|
|
/* RR0 */
|
|
assign rr0_a = { 1'b0, /* Break */
|
|
1'b1, /* Tx Underrun/EOM */
|
|
wr15_a[5] ? cts_latch_a : cts_a, /* CTS */
|
|
1'b0, /* Sync/Hunt */
|
|
wr15_a[3] ? dcd_latch_a : dcd_a, /* DCD */
|
|
//1'b1, /*TX EMPTY */
|
|
~tx_busy_a, /* Tx Empty */
|
|
1'b0, /* Zero Count */
|
|
rx_wr_a_latch /* Rx Available */
|
|
};
|
|
assign rr0_b = { 1'b0, /* Break */
|
|
1'b1, /* Tx Underrun/EOM */
|
|
1'b0, /* CTS */
|
|
1'b0, /* Sync/Hunt */
|
|
wr15_b[3] ? dcd_latch_b : dcd_b, /* DCD */
|
|
1'b1, /* Tx Empty */
|
|
1'b0, /* Zero Count */
|
|
1'b0 /* Rx Available */
|
|
};
|
|
|
|
/* RR1 */
|
|
assign rr1_a = { 1'b0, /* End of frame */
|
|
1'b0,//frame_err_a, /* CRC/Framing error */
|
|
1'b0, /* Rx Overrun error */
|
|
1'b0,//parity_err_a, /* Parity error */
|
|
1'b0, /* Residue code 0 */
|
|
1'b1, /* Residue code 1 */
|
|
1'b1, /* Residue code 2 */
|
|
~tx_busy_a /* All sent */
|
|
};
|
|
|
|
assign rr1_b = { 1'b0, /* End of frame */
|
|
1'b0, /* CRC/Framing error */
|
|
1'b0, /* Rx Overrun error */
|
|
1'b0, /* Parity error */
|
|
1'b0, /* Residue code 0 */
|
|
1'b1, /* Residue code 1 */
|
|
1'b1, /* Residue code 2 */
|
|
1'b1 /* All sent */
|
|
};
|
|
|
|
/* RR2 (Chan B only, A is just WR2) */
|
|
assign rr2_b = { wr2[7],
|
|
wr9[4] ? rr2_vec_stat[0] : wr2[6],
|
|
wr9[4] ? rr2_vec_stat[1] : wr2[5],
|
|
wr9[4] ? rr2_vec_stat[2] : wr2[4],
|
|
wr9[4] ? wr2[3] : rr2_vec_stat[2],
|
|
wr9[4] ? wr2[2] : rr2_vec_stat[1],
|
|
wr9[4] ? wr2[1] : rr2_vec_stat[0],
|
|
wr2[0]
|
|
};
|
|
|
|
|
|
/* RR3 (Chan A only) */
|
|
assign rr3_a = { 2'b0,
|
|
rx_irq_pend_a, /* Rx interrupt pending */
|
|
tx_irq_pend_a, /* Tx interrupt pending */
|
|
ex_irq_pend_a, /* Status/Ext interrupt pending */
|
|
rx_irq_pend_b,
|
|
tx_irq_pend_b,
|
|
ex_irq_pend_b
|
|
};
|
|
|
|
/* RR10 */
|
|
assign rr10_a = { 1'b0, /* One clock missing */
|
|
1'b0, /* Two clocks missing */
|
|
1'b0,
|
|
1'b0, /* Loop sending */
|
|
1'b0,
|
|
1'b0,
|
|
1'b0, /* On Loop */
|
|
1'b0
|
|
};
|
|
assign rr10_b = { 1'b0, /* One clock missing */
|
|
1'b0, /* Two clocks missing */
|
|
1'b0,
|
|
1'b0, /* Loop sending */
|
|
1'b0,
|
|
1'b0,
|
|
1'b0, /* On Loop */
|
|
1'b0
|
|
};
|
|
|
|
/* RR15 */
|
|
assign rr15_a = { wr15_a[7],
|
|
wr15_a[6],
|
|
wr15_a[5],
|
|
wr15_a[4],
|
|
wr15_a[3],
|
|
1'b0,
|
|
wr15_a[1],
|
|
1'b0
|
|
};
|
|
|
|
assign rr15_b = { wr15_b[7],
|
|
wr15_b[6],
|
|
wr15_b[5],
|
|
wr15_b[4],
|
|
wr15_b[3],
|
|
1'b0,
|
|
wr15_b[1],
|
|
1'b0
|
|
};
|
|
|
|
/* Interrupts. Simplified for now
|
|
*
|
|
* 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.
|
|
* TODO: AJS - look at tx and interrupt logic
|
|
*/
|
|
|
|
/*
|
|
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 tx_irq_pend_b = 0 /*& wr1_b[1]*/; /* Tx always empty for now */
|
|
assign ex_irq_pend_b = ex_irq_ip_b;
|
|
|
|
assign _irq = ~(wr9[3] & (rx_irq_pend_a |
|
|
|
|
|
|
rx_irq_pend_b |
|
|
tx_irq_pend_a |
|
|
tx_irq_pend_b |
|
|
ex_irq_pend_a |
|
|
ex_irq_pend_b));
|
|
|
|
/* XXX Verify that... also missing special receive condition */
|
|
assign rr2_vec_stat = rx_irq_pend_a ? 3'b110 :
|
|
tx_irq_pend_a ? 3'b100 :
|
|
ex_irq_pend_a ? 3'b101 :
|
|
rx_irq_pend_b ? 3'b010 :
|
|
tx_irq_pend_b ? 3'b000 :
|
|
ex_irq_pend_b ? 3'b001 : 3'b011;
|
|
|
|
/* External/Status interrupt & latch logic */
|
|
assign do_extreset_a = wreg_a & (rindex == 0) & (wdata[5:3] == 3'b010);
|
|
assign do_extreset_b = wreg_b & (rindex == 0) & (wdata[5:3] == 3'b010);
|
|
|
|
/* Internal IP bit set if latch different from source and
|
|
* corresponding interrupt is enabled in WR15
|
|
*/
|
|
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];
|
|
|
|
/* Latches close when an enabled IP bit is set and latches
|
|
* are currently open
|
|
*/
|
|
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... */);
|
|
|
|
/* "Master" interrupt, set when latch close & WR1[0] is set */
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
ex_irq_ip_a <= 0;
|
|
else if(cep) begin
|
|
if (do_extreset_a)
|
|
ex_irq_ip_a <= 0;
|
|
else if (do_latch_a && wr1_a[0])
|
|
ex_irq_ip_a <= 1;
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
ex_irq_ip_b <= 0;
|
|
else if(cep) begin
|
|
if (do_extreset_b)
|
|
ex_irq_ip_b <= 0;
|
|
else if (do_latch_b && wr1_b[0])
|
|
ex_irq_ip_b <= 1;
|
|
end
|
|
end
|
|
|
|
/* Latch open/close control */
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
latch_open_a <= 1;
|
|
else if(cep) begin
|
|
if (do_extreset_a)
|
|
latch_open_a <= 1;
|
|
else if (do_latch_a)
|
|
latch_open_a <= 0;
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset)
|
|
latch_open_b <= 1;
|
|
else if(cep) begin
|
|
if (do_extreset_b)
|
|
latch_open_b <= 1;
|
|
else if (do_latch_b)
|
|
latch_open_b <= 0;
|
|
end
|
|
end
|
|
|
|
/* Latches proper */
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset) begin
|
|
dcd_latch_a <= 0;
|
|
cts_latch_a <= 0;
|
|
/* cts ... */
|
|
end else if(cep) begin
|
|
if (do_latch_a)
|
|
dcd_latch_a <= dcd_a;
|
|
cts_latch_a <= cts_a;
|
|
/* cts ... */
|
|
end
|
|
end
|
|
always@(posedge clk or posedge reset) begin
|
|
if (reset) begin
|
|
dcd_latch_b <= 0;
|
|
/* cts ... */
|
|
end else if(cep) begin
|
|
if (do_latch_b)
|
|
dcd_latch_b <= dcd_b;
|
|
/* cts ... */
|
|
end
|
|
end
|
|
|
|
|
|
|
|
/* 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
|