Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)
17245 Discussions

RS232 chargen reset problem

Altera_Forum
Honored Contributor II
2,036 Views

Hello! 

 

I'm actually a software engineer, new to the 'dark arts'! Anyway I'm trying to make a 'chargen' over RS232, and I have succeeded in making it transmit ASCII characters 0x21 to 0x7e. 

 

But I am trying to make it transmit this again each time a button is pressed, to no avail. I am targetting a Cyclone II EP2C8Q208C8N and using a USB-to-RS232 adapter. 

 

When I program the FPGA, the characters are output, then nothing (as expected). But then pressing the reset button does nothing. I have tried running it through ModelSim and found nothing untoward. 

 

Can anyone offer any suggestions? Also I can't post the code because apparently I can't post links :confused: 

 

Thanks,
0 Kudos
5 Replies
Altera_Forum
Honored Contributor II
1,249 Views

Apparently if I post an at-sign, even in a CODE block, the forum thinks i'm posting a link, even if I untick "Automatically parse links in text". Anyway I replaced them with *AT* 

 

module rs232_send ( input wire SYSCLK, // 50MHz input wire RST_B, // reset switch, active low output reg UART_TX ); reg COUNTER, COUNTER_N; wire SER_CLK; reg TX_STATE, TX_STATE_N; reg LETTER, LETTER_N; wire ENABLE; reg UART_TX_N; initial begin COUNTER <= 0; TX_STATE <= 0; TX_STATE_N <= 0; LETTER <= 7'h21; // start with '!' character LETTER_N <= 7'h21; UART_TX <= 1; end assign SER_CLK = (COUNTER == 9'd434); // 50MHz / 434 = 115207 (close enough) assign ENABLE = (LETTER != 7'h7e); always *AT* (posedge SYSCLK or negedge RST_B) begin if(!RST_B) begin COUNTER <= 0; TX_STATE <= 0; LETTER <= 7'h21; UART_TX <= 1; end else begin COUNTER <=# 1 COUNTER_N; TX_STATE <=# 1 TX_STATE_N; LETTER <=# 1 LETTER_N; UART_TX <=# 1 UART_TX_N; end end always *AT* (*) begin if(COUNTER == 9'd434) COUNTER_N = 0; else COUNTER_N = COUNTER + 9'h1; end always *AT* (*) begin if(SER_CLK) begin case(TX_STATE) 4'd0: UART_TX_N = 0; // start bit 4'd1: UART_TX_N = LETTER; // first nibble 4'd2: UART_TX_N = LETTER; 4'd3: UART_TX_N = LETTER; 4'd4: UART_TX_N = LETTER; 4'd5: UART_TX_N = LETTER; // second nibble start 4'd6: UART_TX_N = LETTER; 4'd7: UART_TX_N = LETTER; 4'd8: UART_TX_N = 0; // top bit is always zero, as ASCII is 7-bit 4'd9: UART_TX_N = 1; // stop bit default: UART_TX_N = 1; // idle endcase end else UART_TX_N = UART_TX; end always *AT* (posedge SYSCLK) begin if(SER_CLK & ENABLE) begin if(TX_STATE == 4'd9) begin TX_STATE_N <= 4'h0; if(LETTER == 7'h7e) LETTER_N <= 7'h21; else LETTER_N <= LETTER + 7'h1; end else begin TX_STATE_N <= TX_STATE + 4'h1; LETTER_N <= LETTER; end end end endmodule
0 Kudos
Altera_Forum
Honored Contributor II
1,249 Views

A simple reason could be your RST_B signal is not correctly assigned to the right fpga pin. Serial communications starts upon fpga programming because registers are initially in the correct state. 

Anyway your code is pretty bad: you'd better synchronizing everything to SYSCLK, namely use a single process always @ (posedge SYSCLK) 

There's no point in triggering on RST_B negedge, when you can simply sample it with the fast sysclk; and assigning registers inside a always @ (*) process is not a recommended practice: how is this supposed to be synthesized in real hardware? Latches? Flip-flops, but which clock?
0 Kudos
Altera_Forum
Honored Contributor II
1,249 Views

Thank you for the tips! :) I reworked it a little and removed the always * block, and moved it into a single process, as you suggested. And now it works as expected :D 

 

module rs232_send ( input wire SYSCLK, // 50MHz input wire RST_B, // reset switch, active low output reg UART_TX ); reg COUNTER; wire SER_CLK; reg TX_STATE; reg LETTER; wire ENABLE; initial begin COUNTER <= 0; TX_STATE <= 0; LETTER <= 7'h21; // start with '!' character UART_TX <= 1; end assign SER_CLK = (COUNTER == 16'd5208); // 50MHz / 5208 = 9600 assign ENABLE = (LETTER != 7'h7e); always *AT* (posedge SYSCLK) begin if(!RST_B) begin COUNTER <= 0; TX_STATE <= 4'hA; LETTER <= 7'h21; UART_TX <= 1; end else begin if(!ENABLE) UART_TX <= 1; else if(SER_CLK) begin COUNTER <= 0; case(TX_STATE) 4'd0: UART_TX <= 0; // start bit 4'd1: UART_TX <= LETTER; // first nibble 4'd2: UART_TX <= LETTER; 4'd3: UART_TX <= LETTER; 4'd4: UART_TX <= LETTER; 4'd5: UART_TX <= LETTER; // second nibble start 4'd6: UART_TX <= LETTER; 4'd7: UART_TX <= LETTER; 4'd8: UART_TX <= 0; 4'd9: UART_TX <= 1; // stop bit default: UART_TX <= 1; // idle endcase if(TX_STATE == 4'd9) begin TX_STATE <= 4'h0; if(LETTER == 7'h7e) LETTER <= 7'h21; else LETTER <= LETTER + 7'h1; end else begin TX_STATE <= TX_STATE + 4'h1; end end else COUNTER <= COUNTER + 16'h1; end end endmodule  

 

If there's anything else that's bad about this code, I would be very grateful to hear it. 

 

Thanks again,
0 Kudos
Altera_Forum
Honored Contributor II
1,249 Views

Ok. Now it's fine. 

You could possibly remove these lines 

if(LETTER == 7'h7e) LETTER <= 7'h21; else  

since they are useless: infact when LETTER reaches 7e , ENABLE becomes false and you wouldn't get here any more, until after the RST_B signal has restored LETTER to 7'h21 initial value. 

I'd also replace the if(!ENABLE) statement with the actual test on COUNTER value, because imho it is clearer. 

Please note that your code is already ok as it is. The suggested changes won't affect its functionality but only serves to improve readability and better understanding the way code operates. This may seem not worthy here but it becomes fondamental in a bigger design.
0 Kudos
Altera_Forum
Honored Contributor II
1,249 Views

Thanks again for your help and advice. Despite being a simple project in the grand scheme of things, this has taught me a lot :)

0 Kudos
Reply