@free_electron, as FrankBuss correctly pointed, the 10 bit shit register clocked at 9600hz is not enough as the timing for loading the data to the shift register is a tricky part too.
Haven't done any homework , or thought about what i said , have you ?
ask yourself these questions :
What does a serial port look like when it is in idle ? what is it transmitting ? a 1 ? a 0 ?
So, to reiterate what i said :
Take a 10 bit parallel loadable , serial in , serial out shift register. if you can;t find a 10 bit , make a 12 bit out of 3 four bitters .
Connect serial in to the logic level that is to be transmitted in idle.
Connect bit 0 to the polarity of the start bit, connect bits 1 to 8 to the data you want to transmit and connect bit 9 to the stop bit polarity.
Connect the remaining parallel inputs to the logic level transmitted in idle.
Keep the clock running at all times at 9600 baud.
The shift register now simply cycles the idle bits through its pipe..
The moment you perform a 'parallel write' operation the pipe gets overwritten by your data + start bit + stop bit and the clock ( which is always running does the rest. the load is asynchronous on these shifters. as long as you hold load the data does not move... since the pipe always begins with 'idle' this has the net effect of not doing anything on the serial pin. no corruption occurs.
No need for any stinking counters , no stinking state machines , no stinking and ,or, not, xor, or stinking xnor gates. you don't need anything. you don't even need synchronisation to the 9600 clock. You can be faster or slower. As long as the load pin is active the data does not move in the shift register so the 'idle' level is being broadcast. Only at deassertion of the load pin does the packet begin moving.
Just a 9600 hz clock and a parallel loadable shift register that has enough flipflops to hold 8 bit data + start bit + stop bit.
module simple_tx_uart (input clk9600, serial_in , input [7:0] data_in , output serial_out . input write)
`define idle 1
`define startbit 0
`define stopbit 1
reg [11:0] shifter;
assign serial_out = shifter[11];
always_ff @(posegde clk_9600) begin
shifter [11:0] <= {shifter 10:0, `idle};
if (load) shifter[11:0] = {`idle,`idle,`startbit, data_in[7:0], `stopbit};
endmodule
there you go.
in TTL :
- take 2 74LS165
- tie the two pin's 1 together. this is your load input
- tie the two pins 2 together and feed this a 9600 Hz ttl compatible square wave
- tie all pins 8 to ground
- tie all pins 16 to 5 volts
- tie all pins 15 to ground
- on the first chip : tie pin 10 to the logic level for 'idle'
- tie pin 9 of the first chip to pin pin 10 of the second chip.
- pin 9 of the second chip is your serial out.
- pin 5 and 6 of the second chip are tied to the logic level for idle
- pin 4 of the second chip is tied to the logic level for 'startbit'
- pin 3 of the second chip is tied to data_7
- pin 14 of the second chip is data_6
- pin 13 of the second chip is data_5
- pin 12 of the second chip is data_4
- pin 11 of the second chip is data_3
- pin 6 of the first chip is data_2
- pin 5 of the second chip is data_1
- pin 4 of the second chip is data_0
- pin 3 of the second chip is logic level of stopbit
- pins 14,13,12 and 11 of first chip are tied to the logic level for idle.
GO !
just don't throw data in faster than the shifter can move it. that's a matter of tying a counter in that gets loaded with '16' and counts to '0' a 74193 can do the trick. parallel load all '1' the moment you assert the load on the 165's.
use the'zero' output of the 193 ( i think this counter has a 'zero' output. called 'borrow' ) to tell you : all has left. by tying this borrow output to the clock enable of the 193 it will stop itself when hitting zero.
so you can build this entire thing with 3 ttl ic's.