Commit 4eaaadf0 authored by Lucas Russo's avatar Lucas Russo

hdl/modules/*/wb_rs232/*: add rs232 wishbone master

parent 0e776396
......@@ -6,5 +6,6 @@ modules = { "local" : [
"wb_fmc516",
"wb_ethmac_adapter",
"wb_ethmac",
"wb_dbe_periph"
"wb_dbe_periph",
"wb_rs232_syscon"
] };
files = [ "auto_baud.v",
"rs232_syscon.v",
"rs232_syscon_top.v",
"wb_rs232_syscon.vhd",
"xwb_rs232_syscon.vhd",
"serial.v" ]
//-----------------------------------------------------------------------------
// Auto Baud core
//
// This file is part of the "auto_baud" project.
// http://www.opencores.org/
//
//
// Description: See description below (which suffices for IP core
// specification document.)
//
// Copyright (C) 2002 John Clayton and OPENCORES.ORG
//
// This source file may be used and distributed without restriction provided
// that this copyright statement is not removed from the file and that any
// derivative work contains the original copyright notice and the associated
// disclaimer.
//
// This source file is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This source 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 Lesser General Public
// License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this source.
// If not, download it from http://www.opencores.org/lgpl.shtml
//
//-----------------------------------------------------------------------------
//
// Author: John Clayton
// Date : Aug. 20, 2002
// Update: Aug. 20, 2002 copied this file from rs232_syscon.v (pared down).
// Update: Sep. 4, 2002 First test of this module. The baud rate appears
// to be produced at 1/2 of the desired rate!
// Update: Sep. 5, 2002 First working results. Fixed measurement (shift had
// been left out.) Removed debug port since the unit
// appears to be working fine. It Worked for all of the
// following BAUD rates, using 49.152 MHz clock and
// CLK_FACTOR = 8 and 16 bit main counter:
// 300, 1200, 2400, 9600, 19200, 38400, 57600, 115200.
// Next step is to build the "tracking" version that
// doesn't need a reset to find a new BAUD rate...
// Update: Sep. 13, 2002 Added test data from "auto_baud_with_tracking.v"
// module tests. This module has also been tested
// at various speeds, and it works well.
//
// Description
//-----------------------------------------------------------------------------
// This is a state-machine driven core that measures transition intervals
// in a particular character arriving via rs232 transmission (i.e. PC serial
// port.) Measurements of time intervals between transitions in the received
// character are then used to generate a baud rate clock for use in serial
// communications back and forth with the device that originally transmitted
// the measured character. The clock which is generated is in reality a
// clock enable pulse, one single clock wide, occurring at a rate suitable
// for use in serial communications. (This means that it will be generated
// at 4x or 8x or 16x the actual measured baud rate of the received character.
// The multiplication factor is called "CLOCK_FACTOR_PP" and is a settable
// parameter within this module. The parameter "CLOCK_FACTOR_PP" need not
// be a power of two, but it should be a number between 2 and 16 inclusive.)
//
// The particular character which is targeted for measurement and verification
// in this module is: carriage return (CR) = 0x0d = 13.
// This particular character was chosen because it is frequently used at the
// end of a command line, as entered at a keyboard by a human user interacting
// with a command interpreter. It is anticipated that the user would press
// the "enter" key once upon initializing communications with the electronic
// device, and the resulting carriage return character would be used for
// determining BAUD rate, thus allowing the device to respond at the correct
// rate, and to carry on further communications. The electronic device using
// this "auto_baud" module adjusts its baud rate to match the baud rate of
// the received data. This works for all baud rates, within certain limits,
// and for all system clock rates, within certain limits.
//
// Received serially, the carriage return appears as the following waveform:
// ________ __ ____ _______________
// |__|d0|__|d2d3|________|stop
// start d1 d4d5d6d7
//
// The waveform is shown with an identical "high" time and "low" time for
// each bit. However, actual measurements taken using a logic analyzer
// on characters received from a PC show that the times are not equal.
// The "high" times turned out shorter, and the "low" times longer...
// Therefore, this module attempts to average out this discrepancy by
// measuring one low time and one high time.
//
// Since the transition measurements must unavoidably contain small amounts
// of error, the measurements are made during the beginning 2 bits of
// the received character, (that is, start bit and data bit zero).
// Then the measurement is immediately transformed into a baud rate clock,
// used to verify correct reception of the remaining 8 bits of the character.
// If the entire character is not received correctly using the generated
// baud rate, then the measurement is scrapped, and the unit goes into an
// idle scanning mode waiting for another character to test.
//
// This effectively filters out characters that the unit is not interested in
// receiving (anything that is not a carriage return.) There is a slight
// possibility that a group of other characters could appear by random
// chance in a configuration that resembles a carriage return closely enough
// that the unit might accept the measurement and produce a baud clock too
// low. But the probability of this happening is remote enough that the
// unit is considered highly "robust" in normal use, especially when used
// for command entry by humans. It would take a very clever user indeed, to
// enter the correct series of characters with the correct intercharacter
// timing needed to possibly "fool" the unit!
//
// (Also, the baud rate produced falls within certain limits imposed by
// the hardware of the unit, which prevents the auto_baud unit from mistaking
// a series of short glitches on the serial data line for a really
// fast CR character.)
//
// The first carriage return character received will produce a BAUD rate clock
// and the unit will indicate a "locked" condition. From that point onward,
// the unit will continue running, but it will not scan for any more
// input characters. The only way to reset the unit to its initial condition
// is through the use of the "reset_i" pin. Following reset, the unit is
// once again looking for a carriage return character to lock on to.
// Another module, called "auto_baud_with_tracking.v" handles situations where
// you might want to have the BAUD rate change dynamically.
//
//
// NOTES:
// - This module uses a counter to divide down the clk_i signal to produce the
// baud_clk_o signal. Since the frequency of baud_clk_o is nominally
// CLOCK_FACTOR_PP * rx_baud_rate, where "rx_baud_rate" is the baud rate
// of the received character, then the higher you make CLOCK_FACTOR_PP, the
// higher the generated baud_clk_o signal frequency, and hence the lower the
// resolution of the divider. Therefore, using a lower value for the
// CLOCK_FACTOR_PP will allow you to use a lower clk_i with this module.
// - To set LOG2_MAX_COUNT_PP, remember (max_count*CLOCK_FACTOR_PP)/Fclk_i
// is the maximum measurement time that can be accomodated by the circuit.
// (where Fclk_i is the frequency of clk_i, and 1/Fclk_i is the period.)
// Therefore, set LOG2_MAX_COUNT_PP so that the maximum measurement time
// is at least as long as 2x the baud interval of the slowest received
// serial data (2x because there are two bits involved in the measurement!)
// For example, for Fclk_i = 20MHz, CLOCK_FACTOR_PP = 4 and a minimum
// baud rate of 115,200, you would calculate:
//
// (max_count * CLOCK_FACTOR_PP)*1/Fclk_i >= 2/Fbaud_max
//
// Solving for the bit width of the max_count counter...
//
// LOG2_MAX_COUNT_PP >= ceil(log_base_2(max_count))
//
// >= ceil(log_base_2(2*Fclk_i/(Fbaud_max*CLOCK_FACTOR_PP)))
//
// >= ceil(log_base_2(2*20E6/(115200*4)))
//
// >= ceil(log_base_2(86.8))
//
// >= 7 bits.
//
// - In the above example, the maximum count would approach 87, which means
// that a measurement error of 1 count is about (1/87)=approx. 1.15%. This
// is an acceptable level of error for a baud rate clock. Notice that the
// lower baud rates have an even smaller error percentage (Yes!) but that
// they require a much larger measurement counter... For instance,
// to lock onto 300 baud using the same example above, would require:
//
// LOG2_MAX_COUNT_PP >= ceil(log_base_2(40000000/1200))
//
// >= ceil(log_base_2(33333.3))
//
// >= ceil(15.024678)
//
// >= 16 bits.
//
// - If the percentage error for your highest desired baud rate is greater
// than a few percent, you might want to use a higher Fclk_i or else a
// lower CLOCK_FACTOR_PP.
//
// - Using the default settings: CLK_FACTOR_PP = 8, LOG2_MAX_COUNT_PP = 16
// The following test results were obtained, using an actual session in
// hyperterm, looking for correct readable character transmission both
// directions. (Note: These tests were performed at "human interface"
// speeds. High speed or "back-to-back" character transmission might
// exhibit worse performance than these results.)
// The test results shown below were actually obtained using the more
// complex "auto_baud_with_tracking.v" module. Similar or better results
// are expected with this module.
//
// Clk_i
// Freq.
// (MHz) 110 300 1200 2400 4800 9600 19200 57600 115200
// ------ ---------------------------------------------------------------
// 98 FAIL pass pass pass pass pass pass pass pass
// 55 FAIL pass pass pass pass pass pass pass pass
// 49 pass pass pass pass pass pass pass pass pass
// 24.5 pass pass pass pass pass pass pass pass FAIL
// 12 pass pass pass pass pass pass pass pass FAIL
// 6 pass pass pass pass pass pass pass FAIL FAIL
// 3 pass pass pass pass pass FAIL FAIL FAIL FAIL
// 1.5 pass pass pass pass FAIL FAIL FAIL FAIL FAIL
//
//
//-------------------------------------------------------------------------------------
`define LOG2_MAX_CLOCK_FACTOR 4 // Sets the size of the CLOCK_FACTOR
// prescaler.
`define BITS_PER_CHAR 8 // Include parity, if used, but not
// start and stop bits...
// Note: Simply changing the template bits does not reconfigure the
// module to look for a different character (because a new measurement
// window would have to be defined for a different character...)
// The template bits are the exact bits used during verify, against
// which the incoming character is checked.
// The LSB of the character is discarded, and the stop bit is appended
// since it is the last bit used during verify.
//
// so, for N:8:1 (no parity, 8 data bits, 1 stop bit) it is:
// = {1,(character>>1)} = 9'h086
// or, with parity included it is:
// = {1,parity_bit,(character>>1)} = 9'h106
//
`define TEMPLATE_BITS 9'h086 // Carriage return && termination flag
module auto_baud
(
clk_i,
reset_i,
serial_dat_i,
auto_baud_locked_o,
baud_clk_o
);
// Parameters
// CLOCK_FACTOR_PP can be from [2..16] inclusive.
parameter CLOCK_FACTOR_PP = 8; // Baud clock multiplier
parameter LOG2_MAX_COUNT_PP = 16; // Bit width of measurement counter
// State encodings, provided as parameters
// for flexibility to the one instantiating the module.
// In general, the default values need not be changed.
// There is one state machines: m1.
// "default" state upon power-up and configuration is:
// "m1_idle" because that is the all zero state.
parameter m1_idle = 4'h0; // Initial state (scanning)
parameter m1_measure_0 = 4'h1; // start bit (measuring)
parameter m1_measure_1 = 4'h2; // debounce (measuring)
parameter m1_measure_2 = 4'h3; // data bit 0 (measuring)
parameter m1_measure_3 = 4'h4; // debounce (measuring)
parameter m1_measure_4 = 4'h5; // measurement done (headed to verify)
parameter m1_verify_0 = 4'h8; // data bit 1 (verifying)
parameter m1_verify_1 = 4'h9; // data bit 2 (verifying)
parameter m1_run = 4'h6; // running
parameter m1_verify_failed = 4'h7; // resetting (headed back to idle)
// I/O declarations
input clk_i; // System clock input
input reset_i; // Reset signal for this module
input serial_dat_i; // TTL level serial data signal
output auto_baud_locked_o; // Indicates BAUD clock is being generated
output baud_clk_o; // BAUD clock output (actually a clock enable)
// Internal signal declarations
wire mid_bit_count; // During measurement, advances measurement counter
// (Using the mid bit time to advance the
// measurement timer accomplishes a timing "round"
// so that the timing measurement is as accurate
// as possible.)
// During verify, pulses at mid bit time are used
// to signal the state machine to check a data bit.
wire main_count_rollover; // Causes main_count to roll over
wire clock_count_rollover; // Causes clock_count to roll over
// (when clock_count is used as a
// clock_factor prescaler)
wire enable_clock_count; // Logic that determines when clock_count
// should be counting
wire verify_done; // Indicates finish of verification time
reg idle; // Indicates state
reg run; // Indicates state
reg measure; // Indicates state
reg clear_counters; // Pulses once when measurement is done.
reg verify; // Indicates state
reg character_miscompare; // Indicates character did not verify
reg [`LOG2_MAX_CLOCK_FACTOR-1:0] clock_count; // Clock_factor prescaler,
// and mid_bit counter.
reg [LOG2_MAX_COUNT_PP-1:0] main_count; // Main counter register
reg [LOG2_MAX_COUNT_PP-1:0] measurement; // Stored measurement count
reg [`BITS_PER_CHAR:0] target_bits; // Character bits to compare
// (lsb is not needed, since it is used up during measurement time,
// but the stop bit and possible parity bit are needed.)
// For the state machine
reg [3:0] m1_state;
reg [3:0] m1_next_state;
//--------------------------------------------------------------------------
// Instantiations
//--------------------------------------------------------------------------
//--------------------------------------------------------------------------
// Module code
//--------------------------------------------------------------------------
// This is the CLOCK_FACTOR_PP prescaler and also mid_bit_count counter
assign enable_clock_count = measure || (verify && main_count_rollover);
always @(posedge clk_i or posedge reset_i)
begin
if (reset_i) clock_count <= 0;
else if (clear_counters) clock_count <= 0;
else if (enable_clock_count)
begin // Must have been clk_i edge
if (clock_count_rollover) clock_count <= 0;
else clock_count <= clock_count + 1;
end
end
// Counter rollover condition
assign clock_count_rollover = (clock_count == (CLOCK_FACTOR_PP-1));
// This condition signals the middle of a bit time, for use during the
// verify stage. Also, this signal is used to advance the main count,
// instead of "clock_count_rollover." This produces an effective "rounding"
// operation for the measurement (which would otherwise "truncate" any
// fraction of time contained in this counter at the instant the measurement
// is finished.
// (The "enable_clock_count" is included in order to make the pulse narrow,
// only one clock wide...)
assign mid_bit_count = (
(clock_count == ((CLOCK_FACTOR_PP>>1)-1))
&& enable_clock_count
);
// This is the main counter. During measurement, it advances once for
// each CLOCK_FACTOR_PP cycles of clk_i. This accumulated measurement
// is then latched into "measurement" when the state machine determines that
// the measurement interval is finished.
// During verify and idle_run times (whenever the baud rate clock is used)
// this counter is allowed to run freely, advancing once each clk_i, but being
// reset when it reaches a total count of "measurement" clock cycles. The
// signal that reset the counter during this type of operation is the baud
// rate clock.
always @(posedge clk_i or posedge reset_i)
begin
if (reset_i) main_count <= 0;
else // must have been clk_i edge
begin
// Clear main count when measurement is done
if (clear_counters) main_count <= 0;
// If measuring, advance once per CLOCK_FACTOR_PP clk_i pulses.
else if (measure && mid_bit_count) main_count <= main_count + 1;
// If verifying or running, check reset conditions,
// otherwise advance always.
else if (verify || run)
begin
if (main_count_rollover) main_count <= 0;
else main_count <= main_count + 1;
end
end
end
// This is a shift register used to provide "target" character bits one at
// a time for verification as they are "received" (sampled) using the
// candidate baud clock.
always @(posedge clk_i or posedge reset_i)
begin
if (reset_i) target_bits <= `TEMPLATE_BITS;
else // must have been a clock edge
begin
if (~verify) target_bits <= `TEMPLATE_BITS;
if (verify && mid_bit_count) target_bits <= {0,(target_bits>>1)};
end
end
// It is done when only the stop bit is left in the shift register.
assign verify_done = (
(target_bits == 1)
&& verify
&& mid_bit_count
);
// This is a flip-flop used to keep track of whether the verify operation
// is succeeding or not. Any target bits that do not match the received
// data at the sampling edge, will cause the verify_failed bit to go high.
// This is what the state machine looks at to determine whether it passed
// or not.
always @(posedge clk_i or posedge reset_i)
begin
if (reset_i) character_miscompare <= 0;
else // Must have been a clock edge
begin
if (idle) character_miscompare <= 0;
if (verify && mid_bit_count
&& (target_bits[0] ^ serial_dat_i)) character_miscompare <= 1;
end
end
// This is the measurement storage latch. The final measured time count
// from main_count is stored in this latch upon completion of the measurement
// interval. The value stored in this latch is used whenever the baud clock
// is being generated, to reset the main count (causing a "rollover").
always @(posedge clk_i or posedge idle)
begin
// Set to all ones during idle (asynchronous).
if (idle) measurement <= -1;
// Otherwise, there must have been a clk_i edge
// When the measurement is done, the counters are cleared, and the time
// interval must be stored before it is cleared away...
// This also causes a store following a failed verify state on the way back
// into idle, but the idle state clears out the false measurement anyway.
else if (clear_counters) measurement <= (main_count>>1);
end
// This is effectively the baud clock signal
// But it is prevented from reaching the output pin during verification...
// It is only allowed out of the module during idle_run state.
assign main_count_rollover = (main_count == measurement);
assign baud_clk_o = (main_count_rollover && run);
// This is state machine m1. It checks the status of the serial_dat_i line
// and coordinates the measurement of the time interval of the first two
// bits of the received character, which is the "measurement interval."
// Following the measurement interval, the state machine enters a new
// phase of bit verification. If the measured time interval is accurate
// enough to measure the remaining 8 bits of the character correctly, then
// the measurement is accepted, and the baud rate clock is driven onto
// the baud_clk_o output pin. Incidentally, the process of verification
// effectively filters out all characters which are not the desired target
// character for measurement. In this case, the target character is the
// carriage return.
// State register
always @(posedge clk_i or posedge reset_i)
begin : m1_state_register
if (reset_i) m1_state <= m1_idle; // asynchronous reset
else m1_state <= m1_next_state;
end
// State transition logic
always @(m1_state
or mid_bit_count
or serial_dat_i
or verify_done
or character_miscompare
)
begin : m1_state_logic
// Default values for outputs. The individual states can override these.
idle <= 1'b0;
run <= 1'b0;
measure <= 1'b0;
clear_counters <= 1'b0;
verify <= 1'b0;
case (m1_state) // synthesis parallel_case
m1_idle :
begin
idle <= 1'b1;
if (serial_dat_i == 0) m1_next_state <= m1_measure_0;
else m1_next_state <= m1_idle;
end
m1_measure_0 :
begin
measure <= 1'b1;
// Check at mid bit time, to make sure serial line is still low...
// (At this time, "mid_bit_count" is simply CLOCK_FACTOR_PP>>1 clk_i's.)
if (mid_bit_count && ~serial_dat_i) m1_next_state <= m1_measure_1;
// If it is not low, then it must have been a "glitch"...
else if (mid_bit_count && serial_dat_i) m1_next_state <= m1_idle;
else m1_next_state <= m1_measure_0;
end
m1_measure_1 :
begin
measure <= 1'b1;
// Look for first data bit (high).
if (serial_dat_i) m1_next_state <= m1_measure_2;
// If it is not high keep waiting...
// (Put detection of measurement overflow in here if necessary...)
else m1_next_state <= m1_measure_1;
end
m1_measure_2 :
begin
measure <= 1'b1;
// Check using mid bit time, to make sure serial line is still high...
// (At this time, "mid_bit_count" is simply CLOCK_FACTOR_PP>>1 clk_i's.)
if (mid_bit_count && serial_dat_i) m1_next_state <= m1_measure_3;
// If it is not high, then it must have been a "glitch"...
else if (mid_bit_count && ~serial_dat_i) m1_next_state <= m1_idle;
else m1_next_state <= m1_measure_2;
end
m1_measure_3 :
begin
measure <= 1'b1;
// Look for end of measurement interval (low)
if (!serial_dat_i) m1_next_state <= m1_measure_4;
// If it is not high keep waiting...
// (Put detection of measurement overflow in here if necessary...)
else m1_next_state <= m1_measure_3;
end
// This state outputs a reset pulse, to clear counters and store the
// measurement from main_count.
m1_measure_4 :
begin
clear_counters <= 1'b1; // Clears counters, stores measurement
m1_next_state <= m1_verify_0;
end
m1_verify_0 : // Wait for verify operations to finish
begin
verify <= 1'b1;
if (verify_done) m1_next_state <= m1_verify_1;
else m1_next_state <= m1_verify_0;
end
// NOTE: This "extra" state is needed because the character_miscompare
// information is not valid until 1 cycle after verify_done is
// active.
m1_verify_1 : // Checks for character miscompare
begin
if (character_miscompare) m1_next_state <= m1_verify_failed;
else m1_next_state <= m1_run;
end
m1_verify_failed : // Resets counters on the way back to idle
begin
clear_counters <= 1'b1;
m1_next_state <= m1_idle;
end
// This state is for successful verification results!
// Since this is a single measurement unit, only reset can exit this
// state.
m1_run :
begin
run <= 1'b1;
m1_next_state <= m1_run;
end
default : m1_next_state <= m1_idle;
endcase
end
assign auto_baud_locked_o = run;
endmodule
//`undef LOG2_MAX_CLOCK_FACTOR
\ No newline at end of file
//-------------------------------------------------------------------------------------
//
// Author: John Clayton
// Date : June 25, 2001
// Update: 6/25/01 copied this file from ps2_mouse.v (pared down).
// Update: 6/07/01 Finished initial coding efforts.
// Update: 7/19/01 First compilation. Added master_br_o and master_bg_i;
// Update: 7/25/01 Testing. Eliminated msg_active signal. Changed serial.v
// to reflect new handshaking (i.e. "load_request" is now a
// periodic pulse of tx_clk_1x from rs232_tx...)
// Update: 7/30/01 Began coding m2 state machine. Defined response codes.
// Update: 8/01/01 After some testing with m2, merged m2 into m1. Eliminated
// response codes.
// Update: 8/02/01 Tested & measured the single "combined" state machine's
// performance, and "it was found wanting." (The 49.152MHz
// clock frequency was too fast for it...) Created clk_s
// at 49.152/2 MHz, and this worked.
// Update: 8/03/01 Added counter loop to "execute" and "bus_granted" states
// so that multiple bus cycles are generated, at sequential
// addresses. However, the qty field is not cleared before
// being loaded with new characters, which causes problems.
// Update: 8/07/01 Finished debugging. The read print formatting is now
// correct, and the unit appears to operate correctly.
// Many hours were spent puzzling over how to make this work.
// Removed port "debug".
// Update: 8/24/01 Added "master_stb_i" and "master_we_i" inputs and logic.
// Update: 12/13/01 For memory_sizer.v, I lowered the frequency of clk_s down
// to 49.152/4 MHz, so I changed the CLOCK_FACTOR from 8 to 4
// on the rs232 transciever, and this worked fine.
// Update: 9/09/02 Incorporated the "autobaud_with_tracking" module so that
// the serial clock is generated automatically, no matter
// what frequency clk_i is used. The user simply needs to
// press "enter" from the terminal program to synchronize
// the baud rate generator. Changing BAUD rates on the fly
// is also permitted, simply change to a new BAUD rate in the
// terminal program and hit enter.
//
//
//
//
//
// Description
//-------------------------------------------------------------------------------------
// This is a state-machine driven rs232 serial port interface to a "Wishbone"
// type of bus. It is intended to be used as a "Wishbone system controller"
// for debugging purposes. Specifically, the unit allows the user to send
// text commands to the "rs232_syscon" unit, in order to generate read and
// write cycles on the Wishbone compatible bus. The command structure is
// quite terse and spartan in nature, this is for the sake of the logic itself.
// Because the menu-driven command structure is supported without the use of
// dedicated memory blocks (in order to maintain cross-platform portability
// as much as possible) the menus and command responses were kept as small
// as possible. In most cases, the responses from the unit to the user
// consist of a "newline" and one or two visible characters. The command
// structure consists of the following commands and responses:
//
// Command Syntax Purpose
// --------------- ---------------------------------------
// w aaaa dddd xx Write data "dddd" starting at address "aaaa"
// perform this "xx" times at sequential addresses.
// (The quantity field is optional, default is 1).
// r aaaa xx Read data starting from address "aaaa."
// Perform this "xx" times at sequential addresses.
// (The quantity field is optional, default is 1).
// i Send a reset pulse to the system. (initialize).
//
// Response from rs232_syscon Meaning
// -------------------------- ---------------------------------------
// OK Command received and performed. No errors.
// ? Command buffer full, without receiving "enter."
// C? Command not recognized.
// A? Address field syntax error.
// D? Data field syntax error.
// Q? Quantity field syntax error.
// ! No "ack_i", or else "err_i" received from bus.
// B! No "bg_i" received from master.
//
// NOTES on the operation of this unit:
//
// - The unit generates a command prompt which is "-> ".
// - Capitalization is not important.
// - Each command is terminated by the "enter" key (0x0d character).
// Commands are executed as soon as "enter" is received.
// - Trailing parameters need not be re-entered. Their values will
// remain the same as their previous settings.
// - Use of the backspace key is supported, so mistakes can be corrected.
// - The length of the command line is limited to a fixed number of
// characters, as configured by parameter.
// - Fields are separated by white space, including "tab" and/or "space"
// - All numerical fields are interpreted as hexadecimal numbers.
// Decimal is not supported.
// - Numerical field values are retained between commands. If a "r" is issued
// without any fields following it, the previous values will be used. A
// set of "quantity" reads will take place at sequential addresses.
// If a "w" is issued without any fields following it, the previous data
// value will be written "quantity" times at sequential addresses, starting
// from the next location beyond where the last command ended.
// - If the user does not wish to use "ack" functionality, simply tie the
// "ack_i" input to 1b'1, and then the ! response will never be generated.
// - The data which is read in by the "r" command is displayed using lines
// which begin with the address, followed by the data fields. The number
// of data fields displayed per line (following the address) is adjustable
// by setting a parameter. No other display format adjustments can be made.
// - There is currently only a single watchdog timer. It begins to count at
// the time a user hits "enter" to execute a command. If the bus is granted
// and the ack is received before the expiration of the timer, then the
// cycle will complete normally. Therefore, the watchdog timeout value
// needs to include time for the request and granting of the bus, in
// addition to the time needed for the actual bus cycle to complete.
//
//
// Currently, there is only a single indicator (stb_o) generated during bus
// output cycles which are generated from this unit.
// The user can easily implement decoding logic based upon adr_o and stb_o
// which would serve as multiple "stb_o" type signals for different cores
// which would be sharing the same bus.
//
// The dat_io bus supported by this module is a tri-state type of bus. The
// Wishbone spec. allows for this type of bus (see Wishbone spec. pg. 66).
// However, if separate dat_o and dat_i busses are desired, they can be added
// to the module without too much trouble. Supposedly the only difference
// between the two forms of data bus is that one of them avoids using tri-state
// at the cost of doubling the number of interconnects used to carry data back
// and forth... Some people say that tri-state should be avoided for use
// in internal busses in ASICs. Maybe they are right.
// But in FPGAs tri-state seems to work pretty well, even for internal busses.
//
// Parameters are provided to configure the width of the different command
// fields. To simplify the logic for binary to hexadecimal conversion, these
// parameters allow adjustment in units of 1 hex digit, not anything smaller.
// If your bus has 10 bits, for instance, simply set the address width to 3
// which produces 12 bits, and then just don't use the 2 msbs of address
// output.
//
// No support for the optional Wishbone "retry" (rty_i) input is provided at
// this time.
// No support for "tagn_o" bits is provided at this time, although a register
// might be added external to this module in order to implement to tag bits.
// No BLOCK or RMW cycles are supported currently, so cyc_o is equivalent to
// stb_o...
// The output busses are not tri-stated. The user may add tri-state buffers
// external to the module, using "stb_o" to enable the buffer outputs.
//
//-------------------------------------------------------------------------------------
`define NIBBLE_SIZE 4 // Number of bits in one nibble
// The command register has these values
`define CMD_0 0 // Unused command
`define CMD_I 1 // Initialize (or reset)
`define CMD_R 2 // Read
`define CMD_W 3 // Write
module rs232_syscon (
clk_i,
reset_i,
ack_i,
err_i,
master_bg_i,
master_adr_i,
master_stb_i,
master_we_i,
rs232_rxd_i,
data_in,
data_out,
//dat_io,
rst_o,
master_br_o,
stb_o,
cyc_o,
adr_o,
we_o,
rs232_txd_o
);
// Parameters
// The timer value can be from [0 to (2^WATCHDOG_TIMER_BITS_PP)-1] inclusive.
// RD_FIELDS_PP can be from [0 to (2^RD_FIELD_CTR_BITS_PP)-1] inclusive.
// Ensure that (2^CHAR_COUNT_BITS_PP) >= CMD_BUFFER_SIZE_PP.
// The setting of CMD_BUFFER_SIZE_PP should be large enough to hold the
// largest command, obviously.
// Ensure that (2^RD_DIGIT_COUNT_BITS_PP) is greater than or equal to the
// larger of {ADR_DIGITS_PP,DAT_DIGITS_PP}.
parameter ADR_DIGITS_PP = 8; // # of hex digits for address.
parameter DAT_DIGITS_PP = 8; // # of hex digits for data.
parameter QTY_DIGITS_PP = 2; // # of hex digits for quantity.
parameter CMD_BUFFER_SIZE_PP = 64; // # of chars in the command buffer.
parameter CMD_PTR_BITS_PP = 5; // # of Bits in command buffer ptr.
parameter WATCHDOG_TIMER_VALUE_PP = 65000; //200; // # of sys_clks before ack expected.
parameter WATCHDOG_TIMER_BITS_PP = 16; //8; // # of bits needed for timer.
parameter RD_FIELDS_PP = 8; // # of fields/line (when qty > 1).
parameter RD_FIELD_COUNT_BITS_PP = 4; // # of bits in the fields counter.
parameter RD_DIGIT_COUNT_BITS_PP = 4; // # of bits in the digits counter.
// State encodings, provided as parameters
// for flexibility to the one instantiating the module.
// In general, the default values need not be changed.
// There is one state machines: m1.
// "default" state upon power-up and configuration is:
// "m1_initial_state"
parameter m1_initial_state = 5'h00;
parameter m1_send_ok = 5'h01; // Sends OK
parameter m1_send_prompt = 5'h02; // Sends "-> "
parameter m1_check_received_char = 5'h03;
parameter m1_send_crlf = 5'h04; // Sends cr,lf
parameter m1_parse_error_indicator_crlf = 5'h05; // Sends cr,lf
parameter m1_parse_error_indicator = 5'h06; // Sends ?
parameter m1_ack_error_indicator = 5'h07; // Sends !
parameter m1_bg_error_indicator = 5'h08; // Sends B!
parameter m1_cmd_error_indicator = 5'h09; // Sends C?
parameter m1_adr_error_indicator = 5'h0a; // Sends A?
parameter m1_dat_error_indicator = 5'h0b; // Sends D?
parameter m1_qty_error_indicator = 5'h0c; // Sends Q?
parameter m1_scan_command = 5'h10;
parameter m1_scan_adr_whitespace = 5'h11;
parameter m1_get_adr_field = 5'h12;
parameter m1_scan_dat_whitespace = 5'h13;
parameter m1_get_dat_field = 5'h14;
parameter m1_scan_qty_whitespace = 5'h15;
parameter m1_get_qty_field = 5'h16;
parameter m1_start_execution = 5'h17;
parameter m1_request_bus = 5'h18;
parameter m1_bus_granted = 5'h19;
parameter m1_execute = 5'h1a;
parameter m1_rd_send_adr_sr = 5'h1b;
parameter m1_rd_send_separator = 5'h1c;
parameter m1_rd_send_dat_sr = 5'h1d;
parameter m1_rd_send_space = 5'h1e;
parameter m1_rd_send_crlf = 5'h1f;
// I/O declarations
input clk_i; // System clock input
input reset_i; // Reset signal for this module
input ack_i; // Ack input from Wishbone "slaves"
input err_i; // Err input from Wishbone "slaves"
input master_bg_i; // Bus Grant (grants this module the bus)
// Address bus input from "normal" Wishbone
// master (i.e. from processor)
input [`NIBBLE_SIZE*ADR_DIGITS_PP-1:0] master_adr_i;
input master_stb_i; // bus cycle signal from "normal" bus master
input master_we_i; // write enable from "normal" bus master
input rs232_rxd_i; // Serial data from debug host terminal.
// Data bus (tri-state, to save interconnect)
//inout [`NIBBLE_SIZE*DAT_DIGITS_PP-1:0] dat_io;
input [`NIBBLE_SIZE*DAT_DIGITS_PP-1:0] data_in;
output [`NIBBLE_SIZE*DAT_DIGITS_PP-1:0] data_out;
output rst_o; // Rst output to Wishbone "slaves"
output master_br_o; // Bus request to normal master device.
output stb_o; // Bus cycle indicator to Wishbone "slaves"
output cyc_o; // Bus cycle indicator to Wishbone "slaves"
// Address bus output to Wishbone "slaves"
output [`NIBBLE_SIZE*ADR_DIGITS_PP-1:0] adr_o;
output we_o; // Write enable to Wishbone "slaves"
output rs232_txd_o; // Serial transmit data to debug host terminal
reg rst_o;
reg master_br_o;
// Internal signal declarations
wire watchdog_timer_done; // High when watchdog timer is expired
wire rd_addr_field_done; // High when displayed addr field is complete
wire rd_data_field_done; // High when displayed data field is complete
wire rd_line_done; // High when displayed line is complete
wire char_is_enter; // High when cmd_buffer[char_count] is enter.
wire char_is_whitespace; // High when cmd_buffer[char_count] is whitespace.
wire char_is_num; // High when cmd_buffer[char_count] is 0..9
wire char_is_a_f; // High when cmd_buffer[char_count] is a..f
wire char_is_hex; // High when cmd_buffer[char_count] is a hex char.
wire char_is_r; // High when cmd_buffer[char_count] is r.
wire char_is_w; // High when cmd_buffer[char_count] is w.
wire char_is_i; // High when cmd_buffer[char_count] is i.
wire rx_char_is_enter; // High when rs232_rx_char is enter.
wire rx_char_is_backspace; // High when rs232_rx_char is backspace.
wire [4:0] msg_pointer; // Determines message position or address.
wire [3:0] hex_digit; // This is the digit to be stored.
reg rs232_echo; // High == echo char's received.
reg [7:0] msg_char; // Selected response message character.
reg [4:0] msg_base; // Added to msg_offset to form msg_pointer.
reg [4:0] msg_offset; // Offset from start of message.
reg reset_msg_offset; // High == set message offset to zero
reg incr_msg_offset; // Used for output messages.
reg cmd_i; // Sets command.
reg cmd_r; // Sets command.
reg cmd_w; // Sets command.
reg shift_rd_adr; // Shifts the rd_adr_sr by one character.
reg store_adr; // Allows adr_sr to store hex_digit.
reg store_dat; // Allows dat_sr to store hex_digit.
reg store_qty; // Allows qty_sr to store hex_digit.
reg reset_adr; // Clears adr_sr
reg reset_dat; // Clears dat_sr
reg reset_qty; // Clears qty_sr
reg init_qty; // Sets qty_sr to 1
reg capture_dat; // Puts dat_io into dat_sr for later display.
// For the buses
wire [`NIBBLE_SIZE*ADR_DIGITS_PP-1:0] adr_ptr; // = adr_sr + adr_offset
reg stb_l; // "local" stb signal (to distinguish from stb_o)
reg we_l; // "local" we signal (to distinguish from we_o)
reg [`NIBBLE_SIZE*ADR_DIGITS_PP-1:0] rd_adr_sr; // sr for printing addresses
reg [`NIBBLE_SIZE*ADR_DIGITS_PP-1:0] adr_sr; // "nibble" shift register
reg [`NIBBLE_SIZE*DAT_DIGITS_PP-1:0] dat_sr; // "nibble" shift register
reg [`NIBBLE_SIZE*QTY_DIGITS_PP-1:0] qty_sr; // "nibble" shift register
reg [1:0] command;
reg [`NIBBLE_SIZE*QTY_DIGITS_PP-1:0] adr_offset; // counts from 0 to qty_sr
reg reset_adr_offset;
reg incr_adr_offset;
// For the command buffer
reg [CMD_PTR_BITS_PP-1:0] cmd_ptr; // Offset from start of command.
reg reset_cmd_ptr; // High == set command pointer to zero.
reg incr_cmd_ptr; // Used for "write port" side of the command buffer
reg decr_cmd_ptr; // Used for "write port" side of the command buffer
reg cmd_buffer_write;
reg [7:0] cmd_buffer [0:CMD_BUFFER_SIZE_PP-1];
wire [7:0] cmd_char;
wire [7:0] lc_cmd_char; // Lowercase version of cmd_char
// For the state machine
reg [4:0] m1_state;
reg [4:0] m1_next_state;
// For various counters
reg reset_rd_field_count;
reg reset_rd_digit_count;
reg incr_rd_field_count;
reg incr_rd_digit_count;
reg [RD_FIELD_COUNT_BITS_PP-1:0] rd_field_count; // "fields displayed"
reg [RD_DIGIT_COUNT_BITS_PP-1:0] rd_digit_count; // "digits displayed"
reg [WATCHDOG_TIMER_BITS_PP-1:0] watchdog_timer_count;
reg reset_watchdog;
// For the rs232 interface
wire serial_clk;
wire [2:0] rs232_rx_error;
wire rs232_tx_load;
wire rs232_tx_load_request;
wire rs232_rx_data_ready;
wire [7:0] rs232_rx_char;
wire [7:0] rs232_tx_char; // Either rs232_rx_char or msg_char
//--------------------------------------------------------------------------
// Instantiations
//--------------------------------------------------------------------------
// These defines are for the rs232 interface
`define START_BITS 1
`define DATA_BITS 8
`define STOP_BITS 1
`define CLOCK_FACTOR 8
// This module generates a serial BAUD clock automatically.
// The unit synchronizes on the carriage return character, so the user
// only needs to press the "enter" key for serial communications to start
// working, no matter what BAUD rate and clk_i frequency are used!
auto_baud #(
`CLOCK_FACTOR, // CLOCK_FACTOR_PP
16 // LOG2_MAX_COUNT_PP
)
clock_unit_2
(
.clk_i(clk_i),
.reset_i(reset_i),
.serial_dat_i(rs232_rxd_i),
.auto_baud_locked_o(),
.baud_clk_o(serial_clk)
);
/*
auto_baud_with_tracking #(
`CLOCK_FACTOR, // CLOCK_FACTOR_PP
16 // LOG2_MAX_COUNT_PP
)
clock_unit_2
(
.clk_i(clk_i),
.reset_i(reset_i),
.serial_dat_i(rs232_rxd_i),
.auto_baud_locked_o(),
.baud_clk_o(serial_clk)
);
*/
// A transmitter, which asserts load_request at the end of the currently
// transmitted word. The tx_clk must be a "clock enable" (narrow positive
// pulse) which occurs at 16x the desired transmit rate. If load_request
// is connected directly to load, the unit will transmit continuously.
rs232tx #(
`START_BITS, // start_bits
`DATA_BITS, // data_bits
`STOP_BITS, // stop_bits (add intercharacter delay...)
`CLOCK_FACTOR // clock_factor
)
rs232_tx_block // instance name
(
.clk(clk_i),
.tx_clk(serial_clk),
.reset(reset_i),
.load(rs232_tx_load),
.data(rs232_tx_char),
.load_request(rs232_tx_load_request),
.txd(rs232_txd_o)
);
// A receiver, which asserts "word_ready" to indicate a received word.
// Asserting "read_word" will cause "word_ready" to go low again if it was high.
// The character is held in the output register, during the time the next
// character is coming in.
rs232rx #(
`START_BITS, // start_bits
`DATA_BITS, // data_bits
`STOP_BITS, // stop_bits
`CLOCK_FACTOR // clock_factor
)
rs232_rx_block // instance name
(
.clk(clk_i),
.rx_clk(serial_clk),
.reset(reset_i || (| rs232_rx_error)),
.rxd(rs232_rxd_i),
.read(rs232_tx_load),
.data(rs232_rx_char),
.data_ready(rs232_rx_data_ready),
.error_over_run(rs232_rx_error[0]),
.error_under_run(rs232_rx_error[1]),
.error_all_low(rs232_rx_error[2])
);
//`undef START_BITS
//`undef DATA_BITS
//`undef STOP_BITS
//`undef CLOCK_FACTOR
//--------------------------------------------------------------------------
// Module code
//--------------------------------------------------------------------------
assign adr_o = master_bg_i?adr_ptr:master_adr_i;
assign we_o = master_bg_i?we_l:master_we_i;
assign stb_o = master_bg_i?stb_l:master_stb_i;
//assign dat_io = (master_bg_i && we_l && stb_l)?
//dat_sr:{`NIBBLE_SIZE*DAT_DIGITS_PP{1'bZ}};
assign data_out = (master_bg_i && we_l && stb_l)?
dat_sr:{`NIBBLE_SIZE*DAT_DIGITS_PP{1'bZ}};
//assign data_in = (master_bg_i && we_l && stb_l)?
// dat_sr:{`NIBBLE_SIZE*DAT_DIGITS_PP{1'bZ}};
// Temporary
assign cyc_o = stb_o; // Separate cyc_o is not yet supported!
// This is the adress offset counter
always @(posedge clk_i)
begin
if (reset_i || reset_adr_offset) adr_offset <= 0;
else if (incr_adr_offset) adr_offset <= adr_offset + 1;
end
// This forms the adress pointer which is used on the bus.
assign adr_ptr = adr_sr + adr_offset;
// This is the ROM for the ASCII characters to be transmitted.
always @(msg_pointer)
begin
case (msg_pointer) // synthesis parallel_case
5'b00000 : msg_char <= "0"; // Hexadecimal characters
5'b00001 : msg_char <= "1";
5'b00010 : msg_char <= "2";
5'b00011 : msg_char <= "3";
5'b00100 : msg_char <= "4";
5'b00101 : msg_char <= "5";
5'b00110 : msg_char <= "6";
5'b00111 : msg_char <= "7";
5'b01000 : msg_char <= "8";
5'b01001 : msg_char <= "9";
5'b01010 : msg_char <= "A"; // Address error indication
5'b01011 : msg_char <= "B";
5'b01100 : msg_char <= "C"; // Command error indication
5'b01101 : msg_char <= "D"; // Data error indication
5'b01110 : msg_char <= "E";
5'b01111 : msg_char <= "F";
5'b10000 : msg_char <= " "; // Space
5'b10001 : msg_char <= ":"; // Colon
5'b10010 : msg_char <= " "; // Space
5'b10011 : msg_char <= "?"; // Parse error indication
5'b10100 : msg_char <= "!"; // ack_i/err_i error indication
5'b10101 : msg_char <= "O"; // "All is well" message
5'b10110 : msg_char <= "K";
5'b10111 : msg_char <= 8'h0d; // Carriage return & line feed
5'b11000 : msg_char <= 8'h0a;
5'b11001 : msg_char <= "-"; // Command Prompt
5'b11010 : msg_char <= ">";
5'b11011 : msg_char <= " ";
5'b11100 : msg_char <= "Q"; // Quantity error indication
default : msg_char <= "=";
endcase
end
// This logic determines when to load a transmit character.
assign rs232_tx_load = rs232_echo?
(rs232_rx_data_ready && rs232_tx_load_request):rs232_tx_load_request;
// This is the counter for incrementing, decrementing or resetting the
// message pointer.
always @(posedge clk_i)
begin
if (reset_i || reset_msg_offset) msg_offset <= 0;
else if (incr_msg_offset) msg_offset <= msg_offset + 1;
end
assign msg_pointer = msg_offset + msg_base;
// This is the mux which selects whether to echo back the characters
// received (as during the entering of a command) or to send back response
// characters.
assign rs232_tx_char = (rs232_echo)?rs232_rx_char:msg_char;
// These assigments are for detecting whether the received rs232 character is
// anything of special interest.
assign rx_char_is_enter = (rs232_rx_char == 8'h0d);
assign rx_char_is_backspace = (rs232_rx_char == 8'h08);
// This is state machine m1. It handles receiving the command line, including
// backspaces, and prints error/response messages. It also parses and
// executes the commands.
// State register
always @(posedge clk_i)
begin : m1_state_register
if (reset_i) m1_state <= m1_initial_state; // perform reset for rest of system
else m1_state <= m1_next_state;
end
// State transition logic
always @(m1_state
or rx_char_is_enter
or rx_char_is_backspace
or msg_offset
or cmd_ptr
or rs232_tx_load
or char_is_whitespace
or char_is_hex
or char_is_enter
or char_is_i
or char_is_r
or char_is_w
or command
or master_bg_i
or watchdog_timer_done
or err_i
or ack_i
or adr_offset
or qty_sr
or dat_sr
or rd_adr_sr
or rd_field_count
or rd_digit_count
)
begin : m1_state_logic
// Default values for outputs. The individual states can override these.
msg_base <= 5'b0;
reset_msg_offset <= 0;
incr_msg_offset <= 0;
rs232_echo <= 0;
rst_o <= 0;
we_l <= 0;
stb_l <= 0;
cmd_buffer_write <= 0;
reset_cmd_ptr <= 0;
incr_cmd_ptr <= 0;
decr_cmd_ptr <= 0;
master_br_o <= 0;
cmd_r <= 0;
cmd_w <= 0;
cmd_i <= 0;
shift_rd_adr <= 0;
store_adr <= 0; // enables storing hex chars in adr_sr (shift)
store_dat <= 0; // enables storing hex chars in dat_sr (shift)
store_qty <= 0; // enables storing hex chars in qty_sr (shift)
reset_adr <= 0;
reset_dat <= 0;
reset_qty <= 0;
init_qty <= 0;
capture_dat <= 0; // enables capturing bus data in dat_sr (load)
incr_adr_offset <= 0;
reset_adr_offset <= 0;
reset_watchdog <= 0;
incr_rd_field_count <= 0;
incr_rd_digit_count <= 0;
reset_rd_field_count <= 0;
reset_rd_digit_count <= 0;
case (m1_state) // synthesis parallel_case
m1_initial_state :
begin
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 15) && rs232_tx_load) begin
m1_next_state <= m1_send_prompt;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_initial_state;
end
m1_send_ok :
begin
msg_base <= 5'b10101; // Address of the OK message
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 1) && rs232_tx_load) begin
m1_next_state <= m1_send_prompt;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_send_ok;
end
m1_send_prompt :
begin
msg_base <= 5'b10111; // Address of the cr,lf,prompt message
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 4) && rs232_tx_load) begin
m1_next_state <= m1_check_received_char;
reset_cmd_ptr <= 1;
end
else m1_next_state <= m1_send_prompt;
end
// This state always leads to activating the parser...
m1_send_crlf :
begin
msg_base <= 5'b10111; // Address of the cr/lf message
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 1) && rs232_tx_load) begin
m1_next_state <= m1_scan_command;
reset_cmd_ptr <= 1;
end
else m1_next_state <= m1_send_crlf;
end
m1_check_received_char :
begin
rs232_echo <= 1; // Allow echoing of characters
if (rx_char_is_backspace && rs232_tx_load)
begin
m1_next_state <= m1_check_received_char;
decr_cmd_ptr <= 1; // This effectively eliminates the last char
end
else if (rx_char_is_enter && rs232_tx_load)
begin
m1_next_state <= m1_send_crlf;
cmd_buffer_write <= 1; // Store the enter as "marker" for parsing
reset_msg_offset <= 1;
end
else if (rs232_tx_load && (cmd_ptr == CMD_BUFFER_SIZE_PP-1))
begin
m1_next_state <= m1_parse_error_indicator_crlf;
reset_msg_offset <= 1;
reset_cmd_ptr <= 1;
end
else if (rs232_tx_load)
begin
incr_cmd_ptr <= 1;
cmd_buffer_write <= 1;
m1_next_state <= m1_check_received_char;
end
else m1_next_state <= m1_check_received_char;
end
m1_bg_error_indicator :
begin
msg_base <= 5'b01011; // Address of the B character
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load) begin
m1_next_state <= m1_ack_error_indicator;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_bg_error_indicator;
end
m1_ack_error_indicator :
begin
msg_base <= 5'b10100; // Address of the ! error message
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load) begin
m1_next_state <= m1_send_prompt;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_ack_error_indicator;
end
// This state is used when the line is too long...
m1_parse_error_indicator_crlf :
begin
msg_base <= 5'b10111; // Address of the cr,lf message.
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 1) && rs232_tx_load) begin
m1_next_state <= m1_parse_error_indicator;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_parse_error_indicator_crlf;
end
m1_parse_error_indicator :
begin
msg_base <= 5'b10011; // Address of the ? message.
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load) begin
m1_next_state <= m1_send_prompt;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_parse_error_indicator;
end
m1_cmd_error_indicator :
begin
msg_base <= 5'b01100; // Address of 'C'
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load) begin
m1_next_state <= m1_parse_error_indicator;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_cmd_error_indicator;
end
m1_adr_error_indicator :
begin
msg_base <= 5'b01010; // Address of 'A'
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load)
begin
m1_next_state <= m1_parse_error_indicator;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_adr_error_indicator;
end
m1_dat_error_indicator :
begin
msg_base <= 5'b01101; // Address of 'D'
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load)
begin
m1_next_state <= m1_parse_error_indicator;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_dat_error_indicator;
end
m1_qty_error_indicator :
begin
msg_base <= 5'b11100; // Address of 'Q'
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load)
begin
m1_next_state <= m1_parse_error_indicator;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_qty_error_indicator;
end
// The following states are for parsing and executing the command.
// This state takes care of leading whitespace before the command
m1_scan_command :
begin
rs232_echo <= 1; // Don't send message characters
reset_msg_offset <= 1; // This one reset should cover all of the
// parse/exec. states. With rs232_echo
// on, and no receive characters arrive,
// then the msg_offset will remain reset.
// This means the watchdog timer can take
// a long time, if need be, during exec.
// (NOTE: It might be better to disable
// the echoing of rx chars during these
// states.)
init_qty <= 1; // Set qty = 1 by default. That can be
// overridden later, if the command has
// a different qty field.
if (char_is_whitespace) begin
m1_next_state <= m1_scan_command;
incr_cmd_ptr <= 1;
end
else if (char_is_r) begin
m1_next_state <= m1_scan_adr_whitespace;
incr_cmd_ptr <= 1;
cmd_r <= 1;
end
else if (char_is_w) begin
m1_next_state <= m1_scan_adr_whitespace;
incr_cmd_ptr <= 1;
cmd_w <= 1;
end
else if (char_is_i) begin
m1_next_state <= m1_start_execution;
cmd_i <= 1;
end
else m1_next_state <= m1_cmd_error_indicator;
end
// The only way to determine the end of a valid field is to find
// whitespace. Therefore, char_is_whitespace must be used as an exit
// condition from the "get_xxx_field" states. So, this state is used to
// scan through any leading whitespace prior to it.
m1_scan_adr_whitespace :
begin
rs232_echo <= 1; // Don't send message characters
if (char_is_whitespace) begin
m1_next_state <= m1_scan_adr_whitespace;
incr_cmd_ptr <= 1;
end
else if (char_is_enter) m1_next_state <= m1_start_execution;
else begin
m1_next_state <= m1_get_adr_field;
reset_adr <= 1;
end
end
m1_get_adr_field :
begin
rs232_echo <= 1; // Don't send message characters
if (char_is_hex) begin
m1_next_state <= m1_get_adr_field;
store_adr <= 1;
incr_cmd_ptr <= 1;
end
else if (char_is_whitespace) begin // Normal exit
m1_next_state <= m1_scan_dat_whitespace;
end
else if (char_is_enter) m1_next_state <= m1_start_execution;
else m1_next_state <= m1_adr_error_indicator;
end
m1_scan_dat_whitespace :
begin
rs232_echo <= 1; // Don't send message characters
// There is no DAT field for reads, so skip it.
if (command == `CMD_R) m1_next_state <= m1_scan_qty_whitespace;
else if (char_is_whitespace) begin
m1_next_state <= m1_scan_dat_whitespace;
incr_cmd_ptr <= 1;
end
else if (char_is_enter) m1_next_state <= m1_start_execution;
else begin
m1_next_state <= m1_get_dat_field;
reset_dat <= 1;
end
end
m1_get_dat_field :
begin
rs232_echo <= 1; // Don't send message characters
if (char_is_hex) begin
m1_next_state <= m1_get_dat_field;
store_dat <= 1;
incr_cmd_ptr <= 1;
end
else if (char_is_whitespace) begin // Normal exit
m1_next_state <= m1_scan_qty_whitespace;
end
else if (char_is_enter) m1_next_state <= m1_start_execution;
else m1_next_state <= m1_dat_error_indicator;
end
m1_scan_qty_whitespace :
begin
rs232_echo <= 1; // Don't send message characters
if (char_is_whitespace) begin
m1_next_state <= m1_scan_qty_whitespace;
incr_cmd_ptr <= 1;
end
else if (char_is_enter) m1_next_state <= m1_start_execution;
else begin
m1_next_state <= m1_get_qty_field;
reset_qty <= 1;
end
end
m1_get_qty_field :
begin
rs232_echo <= 1; // Don't send message characters
if (char_is_hex) begin
m1_next_state <= m1_get_qty_field;
store_qty <= 1;
incr_cmd_ptr <= 1;
end
else if (char_is_whitespace || char_is_enter) begin // Normal exit
m1_next_state <= m1_start_execution;
end
else m1_next_state <= m1_qty_error_indicator;
end
// This state seeks to obtain master_bg_i, which grants the bus to
// rs232_syscon.
m1_start_execution :
begin
rs232_echo <= 1; // Don't send message characters
reset_watchdog <= 1; // Reset the timer.
reset_adr_offset <= 1; // Reset the address offset.
reset_rd_field_count <= 1; // Reset the rd_field_count.
m1_next_state <= m1_request_bus;
end
m1_request_bus :
begin
rs232_echo <= 1; // Don't send message characters
master_br_o <= 1; // Request the bus.
if (master_bg_i) m1_next_state <= m1_bus_granted;
else if (watchdog_timer_done) begin
m1_next_state <= m1_bg_error_indicator;
end
else m1_next_state <= m1_request_bus;
end
m1_bus_granted :
begin
rs232_echo <= 1; // Don't send message characters
master_br_o <= 1; // Keep holding the bus
reset_watchdog <= 1; // Reset the timer.
if (adr_offset != qty_sr) m1_next_state <= m1_execute;
else m1_next_state <= m1_send_ok;
end
// This single state does reset/write/read depending upon the value
// contained in "command"!
m1_execute :
begin
rs232_echo <= 1; // Don't send message characters
master_br_o <= 1; // Keep holding the bus
stb_l <= 1'b1; // Show that a bus cycle is happening
case (command) // Assert the appropriate signals
`CMD_I : rst_o <= 1;
`CMD_R : capture_dat <= ack_i;
`CMD_W : we_l <= 1;
default: ;
endcase
if (watchdog_timer_done || err_i) begin
m1_next_state <= m1_ack_error_indicator;
end
else if (ack_i
&& (command == `CMD_R)
&& (rd_field_count == 0)
)
begin
m1_next_state <= m1_rd_send_adr_sr; // Leads to a new address line.
reset_rd_digit_count <= 1;
incr_adr_offset <= 1; // move to the next address
end
else if (ack_i && (command == `CMD_R)) begin
m1_next_state <= m1_rd_send_dat_sr; // Leads to a new data field.
reset_rd_digit_count <= 1;
reset_msg_offset <= 1;
incr_adr_offset <= 1; // move to the next address
end
else if (ack_i) begin
m1_next_state <= m1_bus_granted; // continue to the next cycle
incr_adr_offset <= 1; // move to the next address
end
else m1_next_state <= m1_execute;
end
m1_rd_send_adr_sr :
begin
msg_base <= {1'b0,rd_adr_sr[`NIBBLE_SIZE*ADR_DIGITS_PP-1:
`NIBBLE_SIZE*(ADR_DIGITS_PP-1)]};
if ((rd_digit_count == ADR_DIGITS_PP-1) && rs232_tx_load) begin
m1_next_state <= m1_rd_send_separator;
reset_msg_offset <= 1;
end
else if (rs232_tx_load) begin
shift_rd_adr <= 1;
incr_rd_digit_count <= 1;
m1_next_state <= m1_rd_send_adr_sr;
end
else m1_next_state <= m1_rd_send_adr_sr;
end
m1_rd_send_separator :
begin
msg_base <= 5'b10000; // Address of the separator message
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 2) && rs232_tx_load)
begin
m1_next_state <= m1_rd_send_dat_sr;
reset_rd_digit_count <= 1;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_rd_send_separator;
end
m1_rd_send_dat_sr :
begin
msg_base <= {1'b0,dat_sr[`NIBBLE_SIZE*DAT_DIGITS_PP-1:
`NIBBLE_SIZE*(DAT_DIGITS_PP-1)]};
if (
(rd_digit_count == DAT_DIGITS_PP-1)
&& (rd_field_count == RD_FIELDS_PP-1)
&& rs232_tx_load
)
begin
m1_next_state <= m1_rd_send_crlf;
reset_rd_field_count <= 1;
end
else if ((rd_digit_count == DAT_DIGITS_PP-1) && rs232_tx_load) begin
m1_next_state <= m1_rd_send_space;
incr_rd_field_count <= 1;
end
else if (rs232_tx_load) begin
store_dat <= 1;
incr_rd_digit_count <= 1;
m1_next_state <= m1_rd_send_dat_sr;
end
else m1_next_state <= m1_rd_send_dat_sr;
end
m1_rd_send_space :
begin
msg_base <= 5'b10000; // Address of the space
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 0) && rs232_tx_load) begin
m1_next_state <= m1_bus_granted;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_rd_send_space;
end
m1_rd_send_crlf :
begin
msg_base <= 5'b10111; // Address of the cr/lf message
incr_msg_offset <= rs232_tx_load;
if ((msg_offset == 1) && rs232_tx_load) begin
m1_next_state <= m1_bus_granted;
reset_msg_offset <= 1;
end
else m1_next_state <= m1_rd_send_crlf;
end
default : m1_next_state <= m1_initial_state;
endcase
end
// This is the counter for incrementing or loading the cmd_ptr
always @(posedge clk_i)
begin
if (reset_i || reset_cmd_ptr) cmd_ptr <= 0;
else if (decr_cmd_ptr) cmd_ptr <= cmd_ptr - 1;
else if (incr_cmd_ptr) cmd_ptr <= cmd_ptr + 1;
end
// This is the command buffer writing section
always @(posedge clk_i)
begin
if (rs232_echo && cmd_buffer_write) cmd_buffer[cmd_ptr] <= rs232_rx_char;
end
// This is the command buffer reading section
assign cmd_char = cmd_buffer[cmd_ptr];
assign lc_cmd_char = (cmd_buffer[cmd_ptr] | 8'h20); // lowercase
// These assigments are for detecting whether the cmd_char is
// anything of special interest.
assign char_is_enter = (cmd_char == 8'h0d); // enter
assign char_is_whitespace = (
(cmd_char == " ") // space
|| (cmd_char == 8'h09) // tab
);
assign char_is_num = ((cmd_char>=8'h30)&&(cmd_char<=8'h39));
assign char_is_a_f = ((lc_cmd_char>=8'h61)&&(lc_cmd_char<=8'h66));
assign char_is_hex = ( char_is_num || char_is_a_f );
assign char_is_r = (lc_cmd_char == "r");
assign char_is_w = (lc_cmd_char == "w");
assign char_is_i = (lc_cmd_char == "i");
assign hex_digit = char_is_num?cmd_char[3:0]:(cmd_char[3:0]+9);
// This is the command register. It stores the type of command to execute.
// This is so that the state machine can parse address, data and qty
// into "generic" storage locations, and then when it executes the command,
// it refers back to this register in order to determine what type of
// operation to perform.
always @(posedge clk_i)
begin
if (reset_i) command <= `CMD_0;
else if (cmd_i) command <= `CMD_I;
else if (cmd_r) command <= `CMD_R;
else if (cmd_w) command <= `CMD_W;
end
// This is the "nibble" shift register for the address which is sent character
// by character to the user. It is loaded each time the adr_offset is
// incremented, in order to save the previous address for use in printing
// to the user.
always @(posedge clk_i)
begin
if (reset_i || reset_adr) rd_adr_sr <= 0;
else if (incr_adr_offset) rd_adr_sr <= adr_ptr;
else if (shift_rd_adr) begin
rd_adr_sr[`NIBBLE_SIZE*ADR_DIGITS_PP-1:`NIBBLE_SIZE] <=
rd_adr_sr[`NIBBLE_SIZE*(ADR_DIGITS_PP-1)-1:0];
rd_adr_sr[`NIBBLE_SIZE-1:0] <= {`NIBBLE_SIZE{1'b0}};
end
end
// These are the "nibble" shift registers. They handle loading the
// hexadecimal digits from the command line.
always @(posedge clk_i)
begin
if (reset_i || reset_adr) adr_sr <= 0;
else if (store_adr) begin
adr_sr[`NIBBLE_SIZE*ADR_DIGITS_PP-1:`NIBBLE_SIZE] <=
adr_sr[`NIBBLE_SIZE*(ADR_DIGITS_PP-1)-1:0];
adr_sr[`NIBBLE_SIZE-1:0] <= hex_digit;
end
end
always @(posedge clk_i)
begin
if (reset_i || reset_dat) dat_sr <= 0;
else if (capture_dat) dat_sr <= data_in;
//else if (capture_dat) dat_sr <= data_out;
else if (store_dat) begin
dat_sr[`NIBBLE_SIZE*DAT_DIGITS_PP-1:`NIBBLE_SIZE] <=
dat_sr[`NIBBLE_SIZE*(DAT_DIGITS_PP-1)-1:0];
dat_sr[`NIBBLE_SIZE-1:0] <= hex_digit;
end
end
always @(posedge clk_i)
begin
if (reset_i || reset_qty) qty_sr <= 0;
else if (init_qty) qty_sr <= 1;
else if (store_qty) begin
qty_sr[`NIBBLE_SIZE*QTY_DIGITS_PP-1:`NIBBLE_SIZE] <=
qty_sr[`NIBBLE_SIZE*(QTY_DIGITS_PP-1)-1:0];
qty_sr[`NIBBLE_SIZE-1:0] <= hex_digit;
end
end
// This is the rd_digit_count counter. It is used for counting digits
// displayed of both the adr_sr and dat_sr, so it must be able to count up
// to the extent of the larger of the two...
always @(posedge clk_i)
begin
if (reset_i || reset_rd_digit_count) rd_digit_count <= 0;
else if (incr_rd_digit_count) rd_digit_count <= rd_digit_count + 1;
end
// This is the rd_field_count counter. It is used for counting dat_sr fields
// displayed per line.
always @(posedge clk_i)
begin
if (reset_i || reset_rd_field_count) rd_field_count <= 0;
else if (incr_rd_field_count) rd_field_count <= rd_field_count + 1;
end
// This is the watchdog timer counter
// The watchdog timer is always "enabled" to operate.
always @(posedge clk_i)
begin
if (reset_i || reset_watchdog) watchdog_timer_count <= 0;
else if (~watchdog_timer_done)
watchdog_timer_count <= watchdog_timer_count + 1;
end
assign watchdog_timer_done = (watchdog_timer_count==WATCHDOG_TIMER_VALUE_PP);
endmodule
module rs232_syscon_top_1_0 (
input clk_i,
input reset_i,
input ack_i,
input err_i,
input rs232_rxd_i,
input [31:0]data_in,
output [31:0]data_out,
output rst_o,
output stb_o,
output cyc_o,
output [31:0]adr_o,
output we_o,
output rs232_txd_o,
output [3:0]sel_o
);
wire syscon_master;
assign sel_o[3] = stb_o;
assign sel_o[2] = stb_o;
assign sel_o[1] = stb_o;
assign sel_o[0] = stb_o;
// Auto-baud
// dat_io -> data_in i data_out
// Magistrala
// Adres: 16 bit
// Dane: 32 bit
rs232_syscon i_rs232_syscon (
.clk_i(clk_i),
.reset_i(reset_i),
.ack_i(ack_i),
.err_i(err_i),
.master_bg_i(syscon_master),
.master_adr_i(),
.master_stb_i(),
.master_we_i(),
.rs232_rxd_i(rs232_rxd_i),
//.rs232_rxd_i(),
.data_in(data_in),
.data_out(data_out),
//.dat_io(data),
.rst_o(rst_o),
.master_br_o(syscon_master),
.stb_o(stb_o),
.cyc_o(cyc_o),
.adr_o(adr_o),
.we_o(we_o),
//.rs232_txd_o()
.rs232_txd_o(rs232_txd_o)
);
endmodule
<?xml version="1.0" encoding="utf-8"?>
<wb_driver nazwa="rs232_syscon_top" wersja="1.0">
<interface>
<line nazwa="rs232_rxd_i" typ="input" />
<line nazwa="rs232_txd_o" typ="output" />
</interface>
<wb_interface>
<line nazwa="clk_i" wb_name="wb_clk" />
<line nazwa="reset_i" wb_name="reset" />
<line nazwa="rst_o" wb_name="wb_rst" />
<line nazwa="cyc_o" wb_name="wb_cyc" />
<line nazwa="adr_o" wb_name="wb_adr" />
<line nazwa="data_in" wb_name="wb_data_out" />
<line nazwa="we_o" wb_name="wb_we" />
<line nazwa="stb_o" wb_name="wb_stb" />
<line nazwa="data_out" wb_name="wb_data_in" />
<line nazwa="ack_i" wb_name="wb_ack" />
<line nazwa="err_i" wb_name="wb_err" />
<line nazwa="sel_o" wb_name="wb_sel" />
</wb_interface>
</wb_driver>
//-----------------------------------------------------------------------------
// module: serial.vhd
// This file contains modules for serial I/O.
//
// OVERVIEW of contents: The clock generators are
// configured to provide clocks at 16x the desired transmit
// BAUD rates for RS232 serial I/O. This is done so that
// the rs232rx module can easily share the clock generation
// circuitry of the rs232tx block.
// The clock_gen_select module has a BAUD rate selection
// input field of three bits. This field allows the appropriate
// "clock_factor" clock rate to be produced once and used on many
// transmitters and receivers.
// Alternatively, there is a "clock_gen" block which uses parameters
// to specify the desired values for the DDS and prescaler divide factor.
//
// Author: John Clayton
// Date : Nov. 7, 2000
// Update: Nov. 7, 2000 Created this file, with RS232tx block only
// The rs232tx block has no double buffering.
// Update: Nov. 9, 2000 Separated clock generator circuitry into "clock_gen"
// Update: Nov. 29, 2000 Added rs232rx block
// Update: Nov. 30, 2000 Moved contents of "async_tx" and "async_rx" from
// blocks.vhd to this file.
// Update: May 7, 2001 Translated this file from VHDL to verilog, using
// "xhdl"
// Update: May 8, 2001 Since the output of xhdl was rather "spotty", I have
// fixed errors and filled in the "holes" in the code.
// In addition, I have tried to wean this file from
// its previous use and dependency upon "blocks.v"
// (also translated from VHDL, but consisting of rather
// trivial modules.)
// Update: May 8, 2001 Converted to synchronous resets on all flip-flops.
// Update: May 16, 2001 Re-worked the state machines for rs232rx and rs232tx.
// Update: July 25, 2001 Changed the polarity of q[] in rs232_tx, so that its
// output will be the desired (high) level upon initial
// FPGA configuration, even before or without a reset!
// Update: Jan. 30, 2002 Corrected the formula in STEP 1: Find the ratio.
// (Thanks to Shehryar Shaheen at the University of
// Limerick for pointing out the error.)
// Update: June 28, 2002 Fixed "clock_gen" so that it uses the dds_clk.
//
//----------------------------------------
//
// FORMULATING BAUD RATE SETTINGS
//
//
// This ROM contains BAUD rate selection parameters for
// use with the clock_gen_select block.
// The BAUD clocks which are generated are positive pulses,
// (used as clock enables,) and may be used as general clock enables
// if desired.
// Configuring the clock_gen begins with a "Fclk" MHz basic clock.
// To this is applied a prescaler (with value of dds_prescale_value,)
// followed by a Direct Digital Synthesizer (DDS) frequency
// generator. The DDS has an accumulator that is "dds_bits" bits wide.
// The user should know that there is some "jitter" introduced by the
// DDS, and that the amount of jitter varies depending upon the desired
// output frequency. By increasing "dds_bits" the jitter can be made
// very small. Also, the jitter tends to be smaller for small values of
// "dds_phase_increment"
//
// The following settings apply for these table values:
// rate_select BAUD rate (16x this is generated)
//----------------------------------------
// 000 9600
// 001 19200
// 010 38400
// 011 57600
// 100 115200
// (other values are available)
//
//
//
// In order to generate the parameters for a new clock
// frequency, first find the basic Fclk for your board.
// (Fclk=49.152 MHz in this example.)
// Next, choose the lowest desired BAUD rate.
// (lowest_rate=9600 in this example.)
//
// STEP 1 : Find the ratio
//-------------------------
// First, pick a "clock_factor" for the operation of the rs232 units.
// (16 is traditional, although this hardware supports odd values as
// well. For instance, you could pick a clock_factor of 7, if this
// allows you to generate BAUD rates more exactly from your particular
// Fclk. Try to pick the highest clock_factor that you practically can
// since higher numbers allow the receiver to sample in the middle of
// the received bit more exactly... Also, don't go over 16 unless you
// widen out the counters in the design!)
// So, for this example, let clock_factor=16.
//
// Now, find Fclk/(clock_factor*lowest_rate) = 320 (in this example).
// If this ratio is NOT an integer, then be aware that the
// BAUD rates which you will be able to produce will not be
// exact. For asynchronous communications, the clock frequency need not
// be exact. (As mentioned in "Brute Force DDS method" below.)
// If you cannot make clocks close enough to the BAUD rates you desire,
// then you can try a different "clock_factor" setting.
// Or, alternately, you could just use the "Brute Force DDS method."
// (See below)
//
// Or, if all else fails: get a different clock oscillator!
// If the ratio is an integer, or close to it, then proceed to step 2.
//
// Brute Force DDS method (Still step 1):
//-----------------------
// Simply increase the size of the DDS (increase "dds_bits",)
// and set the prescaler to Ndiv=1 (to pass the clock directly
// to the DDS). Then choose your DDS phase increment values (STEP 3)
// so that you can produce rates which are as close as possible
// to "clock_factor" times the desired BAUD rates. It might not be exact,
// but it is as close as possible. If "dds_bits" is sufficiently large,
// then the resulting BAUD rate clocks can be _extremely_ close to the
// correct frequencies. The Baud rate clocks actually do not need
// to be perfect, they can vary by perhaps 3 or 4 percent from their
// exact frequencies, with an increased risk of bit-errors which result,
// of course.
//
// The formula is: dds_freq_int = (2^dds_bits)*(desired_frequency)/Fclk.
//
// STEP 2 : Find the prescaler value
//----------------------------------
// If the ratio mentioned in STEP 1 is an integer, then divide that integer
// into its prime factors. The product of all of the prime factors which
// are not equal to 2 is a good place to start for the prescaler_value.
// If this yields too low of a clock frequency going into the DDS, then
// revert to the "Brute force DDS approach" mentioned above, or else get
// a more suitable clock oscillator!
//
// For this example: 320 = 2*2*2*2*2*2*5. So the prescaler Ndiv = 5.
//
// If you are lucky and the prime factors are all equal to 2, then you have
// chosen an Fclk which is very agreeable to producing the BAUD clocks. You
// can probably set Ndiv=1, which disables the prescaler from operating.
//
//
// STEP 3 : Calculate DDS phase increment values
//----------------------------------------------
// Use the following formula:
// dds_freq_int = (2^dds_bits)*(desired_frequency)/Fclk_dds.
// The resulting values should be used with the DDS, and the values will be
// be "dds_bits-1" wide unsigned quantities.
// Remember that you can change the prescaler Ndiv value to get different
// Fclk_dds values.
//
//=========================================================================
//-----------------------------------------
// This component is a generic clock generator. Simply connect the appropriate
// inputs, and the desired frequency will be the result. Output consists of a
// stream of narrow pulses which are one clock wide. The sizes of DDS and
// prescaler counters are adjustable by parameters.
module clock_gen (
clk,
reset,
frequency,
clk_out
);
parameter DDS_PRESCALE_NDIV_PP = 5; // Prescale divide factor
parameter DDS_PRESCALE_BITS_PP = 3;
parameter DDS_BITS_PP = 6;
input clk;
input reset;
input[DDS_BITS_PP-2:0] frequency;
output clk_out;
// Local signals
wire pulse;
wire dds_clk;
// Simple renaming for readability
wire [DDS_BITS_PP-2:0] dds_phase_increment = frequency;
reg delayed_pulse;
reg [DDS_PRESCALE_BITS_PP-1:0] dds_prescale_count;
reg [DDS_BITS_PP-1:0] dds_phase;
// This is the DDS prescaler part. It has a variable divide value.
// The divide factor is "dds_prescale_ndiv".
always @(posedge clk)
begin
if (reset) dds_prescale_count <= 0;
else if (dds_prescale_count == (DDS_PRESCALE_NDIV_PP-1))
dds_prescale_count <= 0;
else dds_prescale_count <= dds_prescale_count + 1;
end
assign dds_clk = (dds_prescale_count == (DDS_PRESCALE_NDIV_PP-1));
// "dds_prescale_count" above could be compared to zero instead, to save
// on logic?...
// This is the DDS phase accumulator part
always @(posedge clk)
begin
if (reset) dds_phase <= 0;
else if (dds_clk) dds_phase <= dds_phase + dds_phase_increment;
end
assign pulse = dds_phase[DDS_BITS_PP-1]; // Simple renaming for readability
// This is "rising edge detector" part
always @(posedge clk)
begin
delayed_pulse <= pulse;
end
assign clk_out = (pulse && ~delayed_pulse); // Choose the rising edge
endmodule
//-----------------------------------------
// This component is a clock generator with parameters selected by an
// index into a lookup table. There are eight possible settings.
// Recalculate the settings for your own needs as described in
// "FORMULATING BAUD RATE SETTINGS" above. You will need to change
// the bit width of the DDS registers, according to the `defines.
`define DDS_BITS 6
`define DDS_PRESCALE_BITS 3
module clock_gen_select (
clk,
reset,
rate_select,
clk_out
);
input clk;
input reset;
input [2:0] rate_select;
output clk_out;
// Local signals
wire pulse;
wire dds_clk;
reg delayed_pulse;
reg [`DDS_PRESCALE_BITS-1:0] dds_prescale_count;
reg [`DDS_PRESCALE_BITS-1:0] dds_prescale_ndiv;
reg [`DDS_BITS-1:0] dds_phase;
reg [`DDS_BITS-2:0] dds_phase_increment;
// This part sets up the "dds_phase_increment" and "dds_prescale_ndiv" values
always @(rate_select)
begin
case (rate_select)
3'b000 : begin
dds_phase_increment <= 1; // 9600
dds_prescale_ndiv <= 5;
end
3'b001 : begin
dds_phase_increment <= 2; // 19200
dds_prescale_ndiv <= 5;
end
3'b010 : begin
dds_phase_increment <= 4; // 38400
dds_prescale_ndiv <= 5;
end
3'b011 : begin
dds_phase_increment <= 6; // 57600
dds_prescale_ndiv <= 5;
end
3'b100 : begin
dds_phase_increment <= 12; // 115200
dds_prescale_ndiv <= 5;
end
3'b101 : begin
dds_phase_increment <= 12; // 115200
dds_prescale_ndiv <= 5;
end
3'b110 : begin
dds_phase_increment <= 12; // 115200
dds_prescale_ndiv <= 5;
end
3'b111 : begin
dds_phase_increment <= 12; // 115200
dds_prescale_ndiv <= 5;
end
default : begin
dds_phase_increment <= 12; // 115200
dds_prescale_ndiv <= 5;
end
endcase
end
// This is the DDS prescaler part. It has a variable divide value.
// The divide factor is "dds_prescale_ndiv" + 1.
always @(posedge clk)
begin
if (reset) dds_prescale_count <= 0;
else if (dds_prescale_count == (dds_prescale_ndiv-1))
dds_prescale_count <= 0;
else dds_prescale_count <= dds_prescale_count + 1;
end
assign dds_clk = (dds_prescale_count == (dds_prescale_ndiv-1));
// "dds_prescale_count" above could be compared to zero?...
// This is the DDS phase accumulator part
always @(posedge clk)
begin
if (reset) dds_phase <= 0;
else if (dds_clk) dds_phase <= dds_phase + dds_phase_increment;
end
assign pulse = dds_phase[`DDS_BITS-1]; // Simple renaming for readability
// This is "rising edge detector" part
always @(posedge clk)
begin
delayed_pulse <= pulse;
end
assign clk_out = (pulse && ~delayed_pulse); // Choose the rising edge
endmodule
//`undef DDS_BITS
//`undef DDS_PRESCALE_BITS
//-----------------------------------------
// This block takes care of receiving an RS232 input word,
// from the "rxd" line in a serial fashion.
// The user is responsible for providing appropriate CLK
// and clock enable (CE) to achieve the desired Baudot interval
// (NOTE: the state machine operates at "CLOCK_FACTOR_PP" times the
// desired BAUD rate. Set it to anything between 2 and 16,
// inclusive. Values higher than 16 will not "buy" much for you,
// and the state machine might not work well for values less than
// four either, because of the difficulty in sampling rxd at the
// "middle" of the bit time. However, it may be useful to adjust
// the clock_factor around in order to generate good BAUD clocks
// from odd Fclk frequencies on your board.)
// Each time the "word_ready" line drives high the unit has put
// a newly received data word into its output buffer, and is possibly
// already in the process of receiving the next one.
// Note that support is not provided for 1.5 stop bits, only integral
// numbers of stop bits are allowed. However, a selection >2 for
// number of stop bits will still work (it will simply receive
// and count additional stop bits before reporting "word_ready"
module rs232rx (
clk,
rx_clk,
reset,
rxd,
read,
data,
data_ready,
error_over_run,
error_under_run,
error_all_low
);
// Parameter declarations
parameter START_BITS_PP = 1;
parameter DATA_BITS_PP = 8;
parameter STOP_BITS_PP = 1;
parameter CLOCK_FACTOR_PP = 16;
// State encodings, provided as parameters
// for flexibility to the one instantiating the module
parameter m1_idle = 0;
parameter m1_start = 1;
parameter m1_shift = 3;
parameter m1_over_run = 2;
parameter m1_under_run = 4;
parameter m1_all_low = 5;
parameter m1_extra_1 = 6;
parameter m1_extra_2 = 7;
parameter m2_data_ready_flag = 1;
parameter m2_data_ready_ack = 0;
// I/O declarations
input clk;
input rx_clk;
input reset;
input rxd;
input read;
output [DATA_BITS_PP-1:0] data;
output data_ready;
output error_over_run;
output error_under_run;
output error_all_low;
reg [DATA_BITS_PP-1:0] data;
reg data_ready;
reg error_over_run;
reg error_under_run;
reg error_all_low;
// Local signal declarations
`define TOTAL_BITS START_BITS_PP + DATA_BITS_PP + STOP_BITS_PP
wire word_xfer_l;
wire mid_bit_l;
wire start_bit_l;
wire stop_bit_l;
wire all_low_l;
reg [3:0] intrabit_count_l;
reg [`TOTAL_BITS-1:0] q;
reg shifter_preset;
reg [2:0] m1_state;
reg [2:0] m1_next_state;
reg m2_state;
reg m2_next_state;
// State register
always @(posedge clk)
begin : m1_state_register
if (reset) m1_state <= m1_idle;
else m1_state <= m1_next_state;
end
always @(m1_state
or reset
or rxd
or mid_bit_l
or all_low_l
or start_bit_l
or stop_bit_l
)
begin : m1_state_logic
// Output signals are low unless set high in a state condition.
shifter_preset <= 0;
error_over_run <= 0;
error_under_run <= 0;
error_all_low <= 0;
case (m1_state)
m1_idle :
begin
shifter_preset <= 1'b1;
if (~rxd) m1_next_state <= m1_start;
else m1_next_state <= m1_idle;
end
m1_start :
begin
if (~rxd && mid_bit_l) m1_next_state <= m1_shift;
else if (rxd && mid_bit_l) m1_next_state <= m1_under_run;
else m1_next_state <= m1_start;
end
m1_shift :
begin
if (all_low_l) m1_next_state <= m1_all_low;
else if (~start_bit_l && ~stop_bit_l) m1_next_state <= m1_over_run;
else if (~start_bit_l && stop_bit_l) m1_next_state <= m1_idle;
else m1_next_state <= m1_shift;
end
m1_over_run :
begin
error_over_run <= 1;
shifter_preset <= 1'b1;
if (reset) m1_next_state <= m1_idle;
else m1_next_state <= m1_over_run;
end
m1_under_run :
begin
error_under_run <= 1;
shifter_preset <= 1'b1;
if (reset) m1_next_state <= m1_idle;
else m1_next_state <= m1_under_run;
end
m1_all_low :
begin
error_all_low <= 1;
shifter_preset <= 1'b1;
if (reset) m1_next_state <= m1_idle;
else m1_next_state <= m1_all_low;
end
default : m1_next_state <= m1_idle;
endcase
end
assign word_xfer_l = ((m1_state == m1_shift) && ~start_bit_l && stop_bit_l);
// State register
always @(posedge clk)
begin : m2_state_register
if (reset) m2_state <= m2_data_ready_ack;
else m2_state <= m2_next_state;
end
// State transition logic
always @(m2_state or word_xfer_l or read)
begin : m2_state_logic
case (m2_state)
m2_data_ready_ack:
begin
data_ready <= 1'b0;
if (word_xfer_l) m2_next_state <= m2_data_ready_flag;
else m2_next_state <= m2_data_ready_ack;
end
m2_data_ready_flag:
begin
data_ready <= 1'b1;
if (read) m2_next_state <= m2_data_ready_ack;
else m2_next_state <= m2_data_ready_flag;
end
default : m2_next_state <= m2_data_ready_ack;
endcase
end
// This counts within a bit-time.
always @(posedge clk)
begin
if (shifter_preset) intrabit_count_l <= 0;
else if (rx_clk)
begin
if (intrabit_count_l == (CLOCK_FACTOR_PP-1)) intrabit_count_l <= 0;
else intrabit_count_l <= intrabit_count_l + 1;
end
end
// This signal gets one "rx_clk" at the middle of the bit time.
assign mid_bit_l = ((intrabit_count_l==(CLOCK_FACTOR_PP / 2)) && rx_clk);
// This is the shift register
always @(posedge clk)
begin : rxd_shifter
if (shifter_preset) q <= -1; // Set to all ones.
else if (mid_bit_l) q <= {rxd,q[`TOTAL_BITS-1:1]};
end
// Note: The definitions of "start_bit_l" and "stop_bit_l" could
// well be updated to include _all_ of the start and stop bits.
assign start_bit_l = q[0];
assign stop_bit_l = q[`TOTAL_BITS-1];
assign all_low_l = ~(| q); // Bit-wise or of the entire shift register
// This is the output buffer
always @(posedge clk)
begin : rxd_output
if (reset) data <= 0;
else if (word_xfer_l)
data <= q[START_BITS_PP+DATA_BITS_PP-1:START_BITS_PP];
end
endmodule
//`undef TOTAL_BITS
//-----------------------------------------
// This block takes care of framing up an RS232 output word,
// and sending it out the "txd" line in a serial fashion.
// The user is responsible for providing appropriate clk
// and clock enable (tx_clk) to achieve the desired Baudot interval
// (a new bit is transmitted each (tx_clk/clock_factor) pulses)
// (NOTE: the state machine operates at "clock_factor" times the
// desired BAUD rate. Set it to anything between 2 and 16,
// inclusive. It may be useful to adjust the clock_factor in order to
// generate good BAUD clocks from odd Fclk frequencies on your board.)
// A load operation is requested by bringing the "load" line high. However,
// the load will only be accepted when load_request is also high (which
// just happens to coincide with tx_clk_1x inside of the state machine...)
// Therefore, the "load_request" line may be used as a bus acknowledgement.
// (load_request = "ack_o" in Wishbone terminology -- but it only lasts for
// one single clk cycle, so be careful how you use it!)
//
// If the "load_request" line is tied to "load," the unit will send
// data characters continuously, with no gaps in between transmissions.
//
// Note that support is not provided for 1.5 stop bits, only integral
// numbers of stop bits are allowed. A selection of more than 2 for
// number of stop bits will still work fine, it will simply introduce
// a delay between characters being transmitted, although the length
// of the transmitter shift register will also grow to include one
// stage for each stop bit requested...
module rs232tx (
clk,
tx_clk,
reset,
load,
data,
load_request,
txd
);
parameter START_BITS_PP = 1;
parameter DATA_BITS_PP = 8;
parameter STOP_BITS_PP = 1;
parameter CLOCK_FACTOR_PP = 16;
parameter TX_BIT_COUNT_BITS_PP = 4; // = ceil(log(total_bits)/log(2)));
// State encodings, provided as parameters
// for flexibility to the one instantiating the module
parameter m1_idle = 0;
parameter m1_waiting = 1;
parameter m1_sending = 3;
parameter m1_sending_last_bit = 2;
// I/O declarations
input clk;
input tx_clk;
input reset;
input load;
input[DATA_BITS_PP-1:0] data;
output load_request;
output txd;
reg load_request;
// local signals
`define TOTAL_BITS START_BITS_PP + DATA_BITS_PP + STOP_BITS_PP
reg [`TOTAL_BITS-1:0] q; // Actual tx shifter
reg [TX_BIT_COUNT_BITS_PP-1:0] tx_bit_count_l;
reg [3:0] prescaler_count_l;
reg [1:0] m1_state;
reg [1:0] m1_next_state;
wire [`TOTAL_BITS-1:0] tx_word = {{STOP_BITS_PP{1'b1}},
data,
{START_BITS_PP{1'b0}}};
wire begin_last_bit;
wire start_sending;
wire tx_clk_1x;
// This is a prescaler to produce the actual transmit clock.
always @(posedge clk)
begin
if (reset) prescaler_count_l <= 0;
else if (tx_clk)
begin
if (prescaler_count_l == (CLOCK_FACTOR_PP-1)) prescaler_count_l <= 0;
else prescaler_count_l <= prescaler_count_l + 1;
end
end
assign tx_clk_1x = ((prescaler_count_l == (CLOCK_FACTOR_PP-1) ) && tx_clk);
// This is the transmitted bit counter
always @(posedge clk)
begin
if (start_sending) tx_bit_count_l <= 0;
else if (tx_clk_1x)
begin
if (tx_bit_count_l == (`TOTAL_BITS-2)) tx_bit_count_l <= 0;
else tx_bit_count_l <= tx_bit_count_l + 1;
end
end
assign begin_last_bit = ((tx_bit_count_l == (`TOTAL_BITS-2) ) && tx_clk_1x);
assign start_sending = (tx_clk_1x && load);
// This state machine handles sending out the transmit data
// State register.
always @(posedge clk)
begin : state_register
if (reset) m1_state <= m1_idle;
else m1_state <= m1_next_state;
end
// State transition logic
always @(m1_state or tx_clk_1x or load or begin_last_bit)
begin : state_logic
// Signal is low unless changed in a state condition.
load_request <= 0;
case (m1_state)
m1_idle :
begin
load_request <= tx_clk_1x;
if (tx_clk_1x && load) m1_next_state <= m1_sending;
else m1_next_state <= m1_idle;
end
m1_sending :
begin
if (begin_last_bit) m1_next_state <= m1_sending_last_bit;
else m1_next_state <= m1_sending;
end
m1_sending_last_bit :
begin
load_request <= tx_clk_1x;
if (load & tx_clk_1x) m1_next_state <= m1_sending;
else if (tx_clk_1x) m1_next_state <= m1_idle;
else m1_next_state <= m1_sending_last_bit;
end
default :
begin
m1_next_state <= m1_idle;
end
endcase
end
// This is the transmit shifter
always @(posedge clk)
begin : txd_shifter
if (reset) q <= 0; // set output to all ones
else if (start_sending) q <= ~tx_word;
else if (tx_clk_1x) q <= {1'b0,q[`TOTAL_BITS-1:1]};
end
assign txd = ~q[0];
endmodule
------------------------------------------------------------------------------
-- Title : Wishbone Ethernet MAC Wrapper
------------------------------------------------------------------------------
-- Author : Lucas Maziero Russo
-- Company : CNPEM LNLS-DIG
-- Created : 2013-26-08
-- Platform : FPGA-generic
-------------------------------------------------------------------------------
-- Description: Wishbone Wrapper for RS232 Master
-------------------------------------------------------------------------------
-- Copyright (c) 2012 CNPEM
-- Licensed under GNU Lesser General Public License (LGPL) v3.0
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2013-26-08 1.0 lucas.russo Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
library work;
use work.wishbone_pkg.all;
use work.dbe_wishbone_pkg.all;
entity wb_rs232_syscon is
generic (
g_ma_interface_mode : t_wishbone_interface_mode := PIPELINED;
g_ma_address_granularity : t_wishbone_address_granularity := BYTE
);
port(
-- WISHBONE common
wb_clk_i : in std_logic;
wb_rstn_i : in std_logic;
-- External ports
rs232_rxd_i : in std_logic;
rs232_txd_o : out std_logic;
-- Reset to FPGA logic
rstn_o : out std_logic;
-- WISHBONE master
m_wb_adr_o : out std_logic_vector(31 downto 0);
m_wb_sel_o : out std_logic_vector(3 downto 0);
m_wb_we_o : out std_logic;
m_wb_dat_o : out std_logic_vector(31 downto 0);
m_wb_dat_i : in std_logic_vector(31 downto 0);
m_wb_cyc_o : out std_logic;
m_wb_stb_o : out std_logic;
m_wb_ack_i : in std_logic;
m_wb_err_i : in std_logic;
m_wb_stall_i : in std_logic;
m_wb_rty_i : in std_logic
);
end wb_rs232_syscon;
architecture rtl of wb_rs232_syscon is
signal rst : std_logic;
signal rst_out : std_logic;
signal m_wb_adr_out : std_logic_vector(31 downto 0);
signal m_wb_sel_out : std_logic_vector(3 downto 0);
signal m_wb_we_out : std_logic;
signal m_wb_dat_out : std_logic_vector(31 downto 0);
signal m_wb_dat_in : std_logic_vector(31 downto 0);
signal m_wb_cyc_out : std_logic;
signal m_wb_stb_out : std_logic;
signal m_wb_ack_in : std_logic;
signal m_wb_err_in : std_logic;
signal m_wb_stall_in : std_logic;
signal m_wb_rty_in : std_logic;
component rs232_syscon_top_1_0
port (
clk_i : in std_logic;
reset_i : in std_logic;
ack_i : in std_logic;
err_i : in std_logic;
rs232_rxd_i : in std_logic;
data_in : in std_logic_vector(31 downto 0);
data_out : out std_logic_vector(31 downto 0);
rst_o : out std_logic;
stb_o : out std_logic;
cyc_o : out std_logic;
adr_o : out std_logic_vector(31 downto 0);
we_o : out std_logic;
rs232_txd_o : out std_logic;
sel_o : out std_logic_vector(3 downto 0)
);
end component;
begin
rst <= not wb_rstn_i;
-- ETHMAC master interface is byte addressed, classic wishbone
cmp_ma_iface_slave_adapter : wb_slave_adapter
generic map (
g_master_use_struct => false,
g_master_mode => g_ma_interface_mode,
g_master_granularity => g_ma_address_granularity,
g_slave_use_struct => false,
g_slave_mode => CLASSIC,
g_slave_granularity => BYTE
)
port map (
clk_sys_i => wb_clk_i,
rst_n_i => wb_rstn_i,
sl_adr_i => m_wb_adr_out,
sl_dat_i => m_wb_dat_out,
sl_sel_i => m_wb_sel_out,
sl_cyc_i => m_wb_cyc_out,
sl_stb_i => m_wb_stb_out,
sl_we_i => m_wb_we_out,
sl_dat_o => m_wb_dat_in,
sl_ack_o => m_wb_ack_in,
sl_stall_o => open,
sl_int_o => open,
sl_rty_o => open,
sl_err_o => m_wb_err_in,
ma_adr_o => m_wb_adr_o,
ma_dat_o => m_wb_dat_o,
ma_sel_o => m_wb_sel_o,
ma_cyc_o => m_wb_cyc_o,
ma_stb_o => m_wb_stb_o,
ma_we_o => m_wb_we_o,
ma_dat_i => m_wb_dat_i,
ma_ack_i => m_wb_ack_i,
ma_stall_i => m_wb_stall_i,
ma_rty_i => m_wb_rty_i,
ma_err_i => m_wb_err_i
);
cmp_rs232_syscon_top_1_0 : rs232_syscon_top_1_0
port map (
clk_i => wb_clk_i,
reset_i => rst,
ack_i => m_wb_ack_in,
err_i => m_wb_err_in,
rs232_rxd_i => rs232_rxd_i,
data_in => m_wb_dat_in,
data_out => m_wb_dat_out,
rst_o => rst_out,
stb_o => m_wb_stb_out,
cyc_o => m_wb_cyc_out,
adr_o => m_wb_adr_out,
we_o => m_wb_we_out,
rs232_txd_o => rs232_txd_o,
sel_o => m_wb_sel_out
);
rstn_o <= not rst_out;
end rtl;
------------------------------------------------------------------------------
-- Title : Wishbone Ethernet MAC Wrapper
------------------------------------------------------------------------------
-- Author : Lucas Maziero Russo
-- Company : CNPEM LNLS-DIG
-- Created : 2013-26-08
-- Platform : FPGA-generic
-------------------------------------------------------------------------------
-- Description: Wishbone Wrapper for RS232 Master
-------------------------------------------------------------------------------
-- Copyright (c) 2012 CNPEM
-- Licensed under GNU Lesser General Public License (LGPL) v3.0
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2013-26-08 1.0 lucas.russo Created
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
library work;
use work.wishbone_pkg.all;
use work.dbe_wishbone_pkg.all;
entity xwb_rs232_syscon is
generic (
g_ma_interface_mode : t_wishbone_interface_mode := PIPELINED;
g_ma_address_granularity : t_wishbone_address_granularity := BYTE
);
port(
-- WISHBONE common
wb_clk_i : in std_logic;
wb_rstn_i : in std_logic;
-- External ports
rs232_rxd_i : in std_logic;
rs232_txd_o : out std_logic;
-- Reset to FPGA logic
rstn_o : out std_logic;
-- WISHBONE master
wb_master_i : in t_wishbone_master_in;
wb_master_o : out t_wishbone_master_out
);
end xwb_rs232_syscon;
architecture rtl of xwb_rs232_syscon is
begin
cmp_wb_rs232_syscon : wb_rs232_syscon
generic map (
g_ma_interface_mode => g_ma_interface_mode,
g_ma_address_granularity => g_ma_address_granularity
)
port map(
-- WISHBONE common
wb_clk_i => wb_clk_i,
wb_rstn_i => wb_rstn_i,
-- External ports
rs232_rxd_i => rs232_rxd_i,
rs232_txd_o => rs232_txd_o,
-- Reset to FPGA logic
rstn_o => rstn_o,
-- WISHBONE master
m_wb_adr_o => wb_master_o.adr,
m_wb_sel_o => wb_master_o.sel,
m_wb_we_o => wb_master_o.we,
m_wb_dat_o => wb_master_o.dat,
m_wb_dat_i => wb_master_i.dat,
m_wb_cyc_o => wb_master_o.cyc,
m_wb_stb_o => wb_master_o.stb,
m_wb_ack_i => wb_master_i.ack,
m_wb_err_i => wb_master_i.err,
m_wb_stall_i => wb_master_i.stall,
m_wb_rty_i => wb_master_i.rty
);
end rtl;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment