1
+ from amaranth import *
2
+ from amaranth .lib import wiring
3
+ from amaranth .lib .wiring import In , Out
4
+
5
+ class UARTReceiver (wiring .Component ):
6
+ """
7
+ A UART (serial) receiver that converts serial data to parallel.
8
+
9
+ UART uses start and stop bits to frame each byte:
10
+ - Line is high when idle
11
+ - Start bit is low (0)
12
+ - 8 data bits follow
13
+ - Stop bit is high (1)
14
+
15
+ Parameters
16
+ ----------
17
+ divisor : int
18
+ Clock divisor for baud rate (system_clock / baud_rate)
19
+ Example: 100MHz system clock, 9600 baud → divisor = 10,417
20
+
21
+ Attributes
22
+ ----------
23
+ i : Signal, in
24
+ Serial input line
25
+ ack : Signal, in
26
+ Acknowledgment (read the received byte)
27
+ data : Signal, out
28
+ 8-bit received data
29
+ rdy : Signal, out
30
+ Data ready flag (high when byte received)
31
+ err : Signal, out
32
+ Error flag (high on framing error)
33
+ """
34
+
35
+ # Interface
36
+ i : In (1 ) # Input bit (serial line)
37
+ data : Out (8 ) # Received byte (parallel output)
38
+ rdy : Out (1 ) # Data ready flag
39
+ ack : In (1 ) # Acknowledgment
40
+ err : Out (1 ) # Error flag
41
+
42
+ def __init__ (self , divisor ):
43
+ super ().__init__ ()
44
+ self .divisor = divisor # Clock divisor for baud rate
45
+
46
+ def elaborate (self , platform ):
47
+ m = Module ()
48
+
49
+ # Baud rate generator
50
+ # This creates a "strobe" (stb) that pulses once per bit period
51
+ ctr = Signal (range (self .divisor )) # Counter for clock division
52
+ stb = Signal () # Strobe signal (pulses when we should sample)
53
+
54
+ # When counter reaches zero, reset it and pulse the strobe
55
+ with m .If (ctr == 0 ):
56
+ m .d .sync += ctr .eq (self .divisor - 1 ) # Reset counter
57
+ m .d .comb += stb .eq (1 ) # Pulse strobe
58
+ with m .Else ():
59
+ m .d .sync += ctr .eq (ctr - 1 ) # Decrement counter
60
+
61
+ # Bit counter (counts 8 data bits)
62
+ bit = Signal (3 ) # 3 bits to count 0-7
63
+
64
+ # FSM (Finite State Machine) for UART reception
65
+ with m .FSM () as fsm :
66
+ # Initial state: wait for start bit
67
+ with m .State ("START" ):
68
+ with m .If (~ self .i ): # If input goes low (start bit detected)
69
+ m .next = "DATA" # Move to DATA state
70
+ m .d .sync += [
71
+ # Sample in middle of bit by setting counter to half divisor
72
+ ctr .eq (self .divisor // 2 ),
73
+ # Prepare to receive 8 bits (bit 7 down to bit 0)
74
+ bit .eq (7 ),
75
+ ]
76
+
77
+ # Receiving data bits
78
+ with m .State ("DATA" ):
79
+ with m .If (stb ): # On each baud strobe (sampling point)
80
+ m .d .sync += [
81
+ bit .eq (bit - 1 ), # Decrement bit counter
82
+ # Cat() concatenates bits - this shifts received bit into the data
83
+ self .data .eq (Cat (self .i , self .data [:- 1 ])),
84
+ ]
85
+ with m .If (bit == 0 ): # If all bits received
86
+ m .next = "STOP" # Move to STOP state
87
+
88
+ # Check stop bit
89
+ with m .State ("STOP" ):
90
+ with m .If (stb ): # On baud strobe
91
+ with m .If (self .i ): # If input is high (valid stop bit)
92
+ m .next = "DONE" # Move to DONE state
93
+ with m .Else (): # If input is low (invalid stop bit)
94
+ m .next = "ERROR" # Move to ERROR state
95
+
96
+ # Data ready - wait for acknowledgment
97
+ with m .State ("DONE" ):
98
+ m .d .comb += self .rdy .eq (1 ) # Set ready flag
99
+ with m .If (self .ack ): # When acknowledged
100
+ m .next = "START" # Go back to START for next byte
101
+
102
+ # Error state - stay here until reset
103
+ # fsm.ongoing() checks if FSM is in a specific state
104
+ m .d .comb += self .err .eq (fsm .ongoing ("ERROR" ))
105
+ with m .State ("ERROR" ):
106
+ pass # Do nothing (stay in error state)
107
+
108
+ return m
109
+
110
+ # Example usage
111
+ if __name__ == "__main__" :
112
+ from amaranth .back import verilog
113
+
114
+ # Create a UART receiver for 9600 baud with a 1MHz clock
115
+ uart = UARTReceiver (divisor = 104 ) # 1,000,000 / 9600 ≈ 104
116
+
117
+ # Generate Verilog
118
+ with open ("uart_rx.v" , "w" ) as f :
119
+ f .write (verilog .convert (uart ))
120
+
121
+ print ("Generated uart_rx.v" )
0 commit comments