uart: new design using FHDL and bank (TX only, incomplete)

This commit is contained in:
Sebastien Bourdeauducq 2011-12-18 00:29:37 +01:00
parent bb21f7584a
commit 6664af73d1
5 changed files with 52 additions and 332 deletions

View File

@ -17,7 +17,6 @@ add_core_files("lm32", ["lm32_cpu.v", "lm32_instruction_unit.v", "lm32_decoder.v
"lm32_interrupt.v", "lm32_ram.v", "lm32_dp_ram.v", "lm32_icache.v", "lm32_interrupt.v", "lm32_ram.v", "lm32_dp_ram.v", "lm32_icache.v",
"lm32_dcache.v", "lm32_top.v", "lm32_debug.v", "lm32_jtag.v", "jtag_cores.v", "lm32_dcache.v", "lm32_top.v", "lm32_debug.v", "lm32_jtag.v", "jtag_cores.v",
"jtag_tap_spartan6.v"]) "jtag_tap_spartan6.v"])
add_core_dir("uart")
os.system("rm -rf build/*") os.system("rm -rf build/*")
os.chdir("build") os.chdir("build")

View File

@ -1,28 +1,56 @@
from functools import partial
from migen.fhdl.structure import * from migen.fhdl.structure import *
from migen.bus import csr from migen.bank.description import *
from migen.bank import csrgen
class Inst: class Inst:
def __init__(self, csr_addr, clk_freq, baud=115200, break_en_default=Constant(0)): def __init__(self, address, clk_freq, baud=115200):
self.bus = csr.Slave("uart") self._rxtx = rxtx = Register("rxtx", BV(8))
declare_signal(self, "tx") divisor = Register("divisor")
declare_signal(self, "rx") self._f_divisor = Field(divisor, "divisor", 8) # TODO: 16
declare_signal(self, "irq") stat = Register("stat") # TODO: autogenerated event manager
declare_signal(self, "brk") self._f_thre = Field(stat, "thre", access_bus=READ_ONLY, access_dev=WRITE_ONLY)
self._inst = Instance("uart",
[("csr_do", self.bus.d_o), self.bank = csrgen.Bank([rxtx, divisor, stat], address=address)
("uart_tx", self.tx), d = partial(declare_signal, self)
("irq", self.irq), d("tx", reset=1)
("break", self.brk)], d("rx")
[("csr_a", self.bus.a_i),
("csr_we", self.bus.we_i), d("_enable16")
("csr_di", self.bus.d_i), d("_enable16_counter", BV(16))
("uart_rx", self.rx)], d("_tx_reg", BV(8))
[("csr_addr", Constant(csr_addr, BV(5))), d("_tx_bitcount", BV(4))
("clk_freq", clk_freq), d("_tx_count16", BV(4))
("baud", baud), d("_tx_busy")
("break_en_default", break_en_default)], self.divisor = int(clk_freq/baud/16); # TODO
"sys_clk",
"sys_rst")
def get_fragment(self): def get_fragment(self):
return Fragment(instances=[self._inst], pads={self.tx, self.rx}) comb = [self._enable16.eq(self._enable16_counter == Constant(0, BV(16)))]
sync = [self._enable16_counter.eq(self._enable16_counter - 1),
If(self._enable16, self._enable16_counter.eq(self.divisor - 1))] # TODO
sync += [If(self._rxtx.dev_re,
self._tx_reg.eq(self._rxtx.dev_r),
self._tx_bitcount.eq(0),
self._tx_count16.eq(1),
self._tx_busy.eq(1),
self.tx.eq(0)
).Elif(self._enable16 & self._tx_busy,
self._tx_count16.eq(self._tx_count16 + 1),
If(self._tx_count16 == Constant(0, BV(4)),
self._tx_bitcount.eq(self._tx_bitcount + 1),
If(self._tx_bitcount == 8,
self.tx.eq(1)
).Elif(self._tx_bitcount == 9,
self.tx.eq(1),
self._tx_busy.eq(0)
).Else(
self.tx.eq(self._tx_reg[0]),
self._tx_reg.eq(Cat(self._tx_reg[1:], 0))
)
)
)]
comb += [self._f_thre.dev_we.eq(1), self._f_thre.dev_w.eq(~self._tx_busy)]
return self.bank.get_fragment() + Fragment(comb, sync, pads={self.tx, self.rx})

2
top.py
View File

@ -21,7 +21,7 @@ def get():
register=True, register=True,
offset=1) offset=1)
uart0 = uart.Inst(0, clk_freq, baud=115200) uart0 = uart.Inst(0, clk_freq, baud=115200)
csrcon0 = csr.Interconnect(wishbone2csr0.csr, [uart0.bus]) csrcon0 = csr.Interconnect(wishbone2csr0.csr, [uart0.bank.interface])
frag = autofragment.from_local() frag = autofragment.from_local()
vns = convtools.Namespace() vns = convtools.Namespace()

View File

@ -1,142 +0,0 @@
/*
* Milkymist SoC
* Copyright (C) 2007, 2008, 2009, 2010, 2011 Sebastien Bourdeauducq
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module uart #(
parameter csr_addr = 5'h0,
parameter clk_freq = 100000000,
parameter baud = 115200,
parameter break_en_default = 1'b0
) (
input sys_clk,
input sys_rst,
input [13:0] csr_a,
input csr_we,
input [7:0] csr_di,
output reg [7:0] csr_do,
output irq,
input uart_rx,
output uart_tx,
output break
);
reg [15:0] divisor;
wire [7:0] rx_data;
wire [7:0] tx_data;
wire tx_wr;
wire uart_tx_transceiver;
uart_transceiver transceiver(
.sys_clk(sys_clk),
.sys_rst(sys_rst),
.uart_rx(uart_rx),
.uart_tx(uart_tx_transceiver),
.divisor(divisor),
.rx_data(rx_data),
.rx_done(rx_done),
.tx_data(tx_data),
.tx_wr(tx_wr),
.tx_done(tx_done),
.break(break_transceiver)
);
assign uart_tx = thru_en ? uart_rx : uart_tx_transceiver;
assign break = break_en & break_transceiver;
/* CSR interface */
wire csr_selected = csr_a[13:9] == csr_addr;
assign irq = (tx_event & tx_irq_en) | (rx_event & rx_irq_en);
assign tx_data = csr_di;
assign tx_wr = csr_selected & csr_we & (csr_a[2:0] == 3'b000);
parameter default_divisor = clk_freq/baud/16;
reg thru_en;
reg break_en;
reg tx_irq_en;
reg rx_irq_en;
reg rx_event;
reg tx_event;
reg thre;
always @(posedge sys_clk) begin
if(sys_rst) begin
divisor <= default_divisor;
csr_do <= 32'd0;
thru_en <= 1'b0;
break_en <= break_en_default;
rx_irq_en <= 1'b0;
tx_irq_en <= 1'b0;
tx_event <= 1'b0;
rx_event <= 1'b0;
thre <= 1'b1;
end else begin
csr_do <= 32'd0;
if(break)
break_en <= 1'b0;
if(tx_done) begin
tx_event <= 1'b1;
thre <= 1'b1;
end
if(tx_wr)
thre <= 1'b0;
if(rx_done) begin
rx_event <= 1'b1;
end
if(csr_selected) begin
case(csr_a[2:0])
3'b000: csr_do <= rx_data;
// TODO 3'b001: csr_do <= divisor;
3'b010: csr_do <= {tx_event, rx_event, thre};
3'b011: csr_do <= {thru_en, tx_irq_en, rx_irq_en};
3'b100: csr_do <= {break_en};
endcase
if(csr_we) begin
case(csr_a[2:0])
3'b000:; /* handled by transceiver */
// TODO 3'b001: divisor <= csr_di[15:0];
3'b010: begin
/* write one to clear */
if(csr_di[1])
rx_event <= 1'b0;
if(csr_di[2])
tx_event <= 1'b0;
end
3'b011: begin
rx_irq_en <= csr_di[0];
tx_irq_en <= csr_di[1];
thru_en <= csr_di[2];
end
3'b100: break_en <= csr_di[0];
endcase
end
end
end
end
endmodule

View File

@ -1,165 +0,0 @@
/*
* Milkymist SoC
* Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
* Copyright (C) 2007 Das Labor
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
module uart_transceiver(
input sys_rst,
input sys_clk,
input uart_rx,
output reg uart_tx,
input [15:0] divisor,
output reg [7:0] rx_data,
output reg rx_done,
input [7:0] tx_data,
input tx_wr,
output reg tx_done,
output reg break
);
//-----------------------------------------------------------------
// enable16 generator
//-----------------------------------------------------------------
reg [15:0] enable16_counter;
wire enable16;
assign enable16 = (enable16_counter == 16'd0);
always @(posedge sys_clk) begin
if(sys_rst)
enable16_counter <= divisor - 16'b1;
else begin
enable16_counter <= enable16_counter - 16'd1;
if(enable16)
enable16_counter <= divisor - 16'b1;
end
end
//-----------------------------------------------------------------
// Synchronize uart_rx
//-----------------------------------------------------------------
reg uart_rx1;
reg uart_rx2;
always @(posedge sys_clk) begin
uart_rx1 <= uart_rx;
uart_rx2 <= uart_rx1;
end
//-----------------------------------------------------------------
// UART RX Logic
//-----------------------------------------------------------------
reg rx_busy;
reg uart_rx_r;
reg [3:0] rx_count16;
reg [3:0] rx_bitcount;
reg [7:0] rx_reg;
always @(posedge sys_clk) begin
if(sys_rst) begin
rx_done <= 1'b0;
rx_busy <= 1'b0;
rx_count16 <= 4'd0;
rx_bitcount <= 4'd0;
break <= 1'b0;
uart_rx_r <= 1'b0;
end else begin
rx_done <= 1'b0;
break <= 1'b0;
if(enable16) begin
uart_rx_r <= uart_rx2;
if(~rx_busy) begin // look for start bit
if(~uart_rx2 & uart_rx_r) begin // start bit found
rx_busy <= 1'b1;
rx_count16 <= 4'd7;
rx_bitcount <= 4'd0;
end
end else begin
rx_count16 <= rx_count16 + 4'd1;
if(rx_count16 == 4'd0) begin // sample
rx_bitcount <= rx_bitcount + 4'd1;
if(rx_bitcount == 4'd0) begin // verify startbit
if(uart_rx2)
rx_busy <= 1'b0;
end else if(rx_bitcount == 4'd9) begin
rx_busy <= 1'b0;
if(uart_rx2) begin // stop bit ok
rx_data <= rx_reg;
rx_done <= 1'b1;
end else if(rx_reg == 8'h00) // break condition
break <= 1'b1;
end else
rx_reg <= {uart_rx2, rx_reg[7:1]};
end
end
end
end
end
//-----------------------------------------------------------------
// UART TX Logic
//-----------------------------------------------------------------
reg tx_busy;
reg [3:0] tx_bitcount;
reg [3:0] tx_count16;
reg [7:0] tx_reg;
always @(posedge sys_clk) begin
if(sys_rst) begin
tx_done <= 1'b0;
tx_busy <= 1'b0;
uart_tx <= 1'b1;
end else begin
tx_done <= 1'b0;
if(tx_wr) begin
tx_reg <= tx_data;
tx_bitcount <= 4'd0;
tx_count16 <= 4'd1;
tx_busy <= 1'b1;
uart_tx <= 1'b0;
`ifdef SIMULATION
$display("UART: %c", tx_data);
`endif
end else if(enable16 && tx_busy) begin
tx_count16 <= tx_count16 + 4'd1;
if(tx_count16 == 4'd0) begin
tx_bitcount <= tx_bitcount + 4'd1;
if(tx_bitcount == 4'd8) begin
uart_tx <= 1'b1;
end else if(tx_bitcount == 4'd9) begin
uart_tx <= 1'b1;
tx_busy <= 1'b0;
tx_done <= 1'b1;
end else begin
uart_tx <= tx_reg[0];
tx_reg <= {1'b0, tx_reg[7:1]};
end
end
end
end
end
endmodule