DE248 Home Page

DE248 Drive Electronics

RS232 PHY OSCILLATOR MODULE

(Last Modified: 04 November 2010 06:09:04 PM )



Top Level Instantiation

Since this is low-level module, the following are just typical-use instantiation templates.

The following is for when operation only at 115,200 baud is desired:

    rs232_phy_osc phy_osc (.tick(baud16), rst(rst), clk(clk));

The following is for when operation operation as slower baud rates is desired:

    rs232_phy_osc phy_osc (.tick(phy_osc), rst(rst), clk(clk));

    pulse_gen_8 rs232_clk (.q(baud16), .period(div), .en(phy_osc),
                           .rst(rst), .clk(clk));

In either case, baud16 is a series of one-master-clock long pulses at sixteen times the baud rate, which is the expected clock signal for the transmit and receive physical layer modules.


User-level Description

This module produces rising edge ticks (pulses that are one system master clock cycle in duration) at a rate as close to 1.843200 MHz, on average, as possible. This is the standard crystal frequency used for typical RS-232 UARTs. This frequency is 16x the nominal "highest" speed of 115,200 baud for common RS-232 communication links.

This module may be used with the RS-232 physical layer transmitter and receiver modules directly if only a single speed of 115,200 baud is needed or with a pulse generator module if slower baud rates are needed.

The value to use for the pulse generator divisor (div in the instantiation template above) is equal to

div = (115200/baud_rate) - 1

The following table gives the necessary value of div for standard baud rates from 50 to 115,200 baud.

baud div
115,200 0
57,600 1
38,400 2
19,200 5
9,600 11
7,200 15
4,800 23
2,400 47
1,200 95
600 191
300 383
150 767
75 1535
50 2303

With an 8-bit pulse generator, the slowest obtainable baud rate is 384 baud. To obtain slower baud rates, simply replace the pulse_gen_8 module with a pulse_gen_16 module, which supports baud rates as low as 1.76 baud.


Module Code

// NOTE: The code in this module is specific to a particular master clock
// frequency and must be redesigned if a different frequency is used.

module rs232_phy_osc(tick, rst, clk);

    output tick; // baud rate oscillator rising edge tick
    input rst, clk;

    parameter lo = 1'b0, hi = 1'b1;

    parameter
        inner_count_period = 5'd26, // For a 50 MHz master clock
        outer_count_period = 3'd7, // For a 50 MHz master clock
        inner_count_zero = 5'd0,
        outer_count_zero = 3'd0;

    reg [4:0] inner_count, next_inner_count;
    reg [2:0] outer_count, next_outer_count;
    reg tick, next_tick;

    always @(posedge clk or posedge rst)
    begin
        if (rst == hi)
            begin
                tick <= lo;
                inner_count <= inner_count_zero;
                outer_count <= outer_count_zero;
            end
        else
            begin
                tick <= next_tick;
                inner_count <= next_inner_count;
                outer_count <= next_outer_count;
        end
    end

    // This code is specific to a 50 MHz master clock.
    always @(inner_count or outer_count)
    begin
        if (inner_count == inner_count_zero)
            begin // Inner loop has expired
                if (outer_count == outer_count_zero)
                    begin // Outer loop has expired
                        next_tick <= hi;
                        next_inner_count <= inner_count_period + 1;
                        next_outer_count <= outer_count_period;
                    end
                else // Outer loop has not expired
                    begin
                        next_tick <= lo;
                        next_inner_count <= inner_count_period;
                        next_outer_count <= outer_count - 1;
                    end
            end
        else
            begin // Inner loop has not expired
                next_tick <= lo;
                next_inner_count <= inner_count - 1;
                next_outer_count <= outer_count;
            end
    end   

endmodule
 


Theory of Operation

The goal of the RS-232 PHY Oscillator module is to produce a sequence of ticks that are as close to the nominal 1.843200 MHz standard crystal frequency as possible given a particular system master clock frequency. This standard target frequency is 16x the "highest" standard RS-232 baud rate of 115,200 baud.

With a master clock frequency of at least 50 MHz, it is always possible to tweak the oscillator so that, over one symbol period at 115,200 baud, the nominal mismatch is less than 1,152 ppm, which will nearly always be sufficient for RS-232 communications since it is an order of magnitude better than the total mismatch that can be tolerated from all sources. However, this is not to say that it is an issue that can be ignored; if the oscillator is configured to merely be a uniform clock at the frequency closest to 1.843200 MHz, then it is possible for each cycle to be up to 10 ns too short or too long resulting in a mismatch of 18,432 ppm which leaves almost no margin for mismatches from other sources, including the oscillator on the other end of the channel.

For the specific case of a 50 MHz master clock, the situation is quite fortuitous. Ignoring the issue completely and using a uniform oscillator period of 27 master clock periods would result in an oscillator running at 1.851852 MHz, which represents a mismatch of approximately -4,700 ppm. It is highly likely that this would be acceptable, especially if the oscillator on the other end of the channel is derived from a standard 1.843200 MHz crystal. However, by simply extending every eighth oscillator period by one master clock period, the result is a nominal oscillator frequency of 1.843180 MHz, which is a mismatch of only -64 ppm compared to the ideal. This is well below the intrinsic tolerance of most commercial crystal oscillators.

The output of the module is the signal tick which is HI for one master clock period just prior to the oscillator's (virtual) rising edge. No actual oscillator output signal is produced as it is sufficient to know when its rising edges would occur and use that information to enable other circuitry that would normally be clocked by the baud rate oscillator. This allows everything to remain fully synchronous to the system master clock thereby eliminating the issues associated with running a separate clock domain.

The parameters, registers, and wires are all sensitive to the master clock frequency. The most obvious issues are the count periods for the inner and outer loops, but less obvious is whether inner and outer loops are even needed and how wide the registers and busses need to be. In the case of a 50 MHz clock, the outer loop has a period of eight and the inner loop has a period of twenty-seven, requiring that the outer loop's registers and busses consist of three bits while the inner loop needs five.

    parameter
        inner_count_period = 5'd26, // For a 50 MHz master clock
        outer_count_period = 3'd7, // For a 50 MHz master clock
        inner_count_zero = 5'd0,
        outer_count_zero = 3'd0;

    reg [4:0] inner_count, next_inner_count;
    reg [2:0] outer_count, next_outer_count;
    reg tick, next_tick;

The first always block in the code is more-or-less completely generic and updates the module's registers by either clearing them in the event of an asynchronous reset or synchronously updating them on the rising edge of the system clock. Since the logic for some master clock frequencies may require something other than two nested loops, it can't be claimed that this block is truly generic.

    always @(posedge clk or posedge rst)
    begin
        if (rst == hi)
            begin
                tick <= lo;
                inner_count <= inner_count_zero;
                outer_count <= outer_count_zero;
            end
        else
            begin
                tick <= next_tick;
                inner_count <= next_inner_count;
                outer_count <= next_outer_count;
        end
    end

The second always block in the code is very specific to the particular system master clock frequency. In the case of a 50 MHz master clock, this block of code is designed to produce seven oscillator ticks with a period of twenty-seven master clock periods followed by an eight that is extended by one additional master clock period. The result is that every sixteen oscillator periods, which corresponds to the highest speed symbol period, total

symbol period @ 115,200 baud = 2*(7*27+1*28)*20ns = 8680 ns

This compares extremely well with the nominal period of 8680.556 ns, in fact it is only -64 ppm away from the ideal and well within the other tolerances that are likely to exist in the system.

The code below produces the desired behavior by maintaining two loops, an inner and an outer. The inner loop decrements by one each master clock cycle until it reaches zero, at which time it is reinitialized (to one of two values as explained shortly) and the outer loop either decrements by one or, if it is zero, is reinitialized to outer_count_period (which is 7). On those occasions when the outer loop simply decrements, the inner loop is reinitialized to inner_count_period (which is 26) and when the outer loop is reinitialized, the inner loop is reinitialized to a value that is one greater than normal (i.e., 27).

    // This code is specific to a 50 MHz master clock.
    always @(inner_count or outer_count)
    begin
        if (inner_count == inner_count_zero)
            begin // Inner loop has expired
                if (outer_count == outer_count_zero)
                    begin // Outer loop has expired
                        next_tick <= hi;
                        next_inner_count <= inner_count_period + 1;
                        next_outer_count <= outer_count_period;
                    end
                else // Outer loop has not expired
                    begin
                        next_tick <= lo;
                        next_inner_count <= inner_count_period;
                        next_outer_count <= outer_count - 1;
                    end
            end
        else
            begin // Inner loop has not expired
                next_tick <= lo;
                next_inner_count <= inner_count - 1;
                next_outer_count <= outer_count;
            end
    end   

endmodule

Note that since inner_count_period and outer_count_period, as well as inner_count_zero and outer_count_zero, are parametric constants they are not included in the signal sensitivity list.