Paul Koning 0f2949dd2b 1. Add bit clock monitor and abort frame reception if clock is lost.
2. Make frequency measurement better for integral modem case.
3. New test mode: raw waveform (for integral modem).
2021-10-17 15:16:22 -04:00

337 lines
11 KiB
Plaintext

// DDCMP sync framer PIO state machines
//
// Copyright (C) 2021 by Paul Koning
// Global defines
// irqs
.define rxbit 4
.define txbit 5
/* The three configuration cases, and the state machines used for
each case, are:
1. RS232 signaling, modem supplied clocks
modem_clk_xmit, modem_clk_recv
2. RS232 signaling, local clock source
local_clk_xmit, local_clk_recv
3. Integral modem
inmodem_xmit, inmodem_rxbit, local_clk_recv
4. Integral modem, raw waveforms
inmodem_rawxmit, inmodem_rxbit, local_clk_recv
Note that these state machines all have to be in the same PIO,
because they communicate via an IRQ. While that is documented
as working across all state machines, in reality it seems to be
local to a given PIO.
In all cases, if an activity LED is wanted, the pulse_stretcher
state machine is also used.
*/
/* Transmit for use with modem sourced clock
This is the transmit pin driver for the case where the modem
supplies the bit clocks, i.e., for use along with the modem_clk
state machine.
in_base is the transmit clock pin (from modem)
out_base is the transmit data pin
out_count is 1
state machine clock: fast (minimally 8x the bit rate).
output fifo threshold is 8 or 32, joined output fifos
Initialization:
Before starting this state machine, use the execute register to
force the execution of these two instructions:
pull
mov x, osr
The data pushed into the output FIFO by the controlling program is
the sync pattern, 0x96969696.
*/
.program modem_clk_xmit
.wrap_target
wait 0 pin 0
wait 1 pin 0
out pins 1
pull ifempty noblock
.wrap
/* Sync search and data receive
This is the receive byte framer, modem clock case
The bit clock used here is the one supplied by the modem.
When searching for sync, no data is sent to the FIFO. But
when sync is found, the sync pattern seen (4 bytes of 0x96)
is pushed to the receive FIFO as a marker. This allows the
mainline code to flush data in the FIFO preceding a resync
without having to stop the state machine to do so. It depends
on the assumption that 4 SYN bytes aren't found in the packet
data past the point where the main code decided to resync; that
is admittedly possible but the probability is low enough not
to worry about it.
in_base is the receive data pin; the receive bit clock
pin must be the next higher pin number.
sideset_base is the "in sync" LED pin
sideset_count is 1 (optional side set)
state machine clock: fast (minimally 8x the bit rate)
input fifo threshold is 32
Initialization:
Before starting this state machine, use the execute register to
force the execution of these three instructions:
pull
mov y, osr
mov isr, null
then set the fifo configuration to joined input fifos. The data
pushed into the output FIFO by the controlling program is the sync
pattern, 0x96969696.
To go back to sync search, jump this state machine back to its
beginning address.
*/
.program modem_clk_recv
.side_set 1 opt
search: wait 1 pin 1 side 0 // indicate not in sync
wait 0 pin 1
in pins, 1
mov x, isr
jmp x!=y, search
push side 1 // indicate in sync
.wrap_target
wait 1 pin 1
wait 0 pin 1
in pins, 1
push iffull noblock
.wrap
/* Transmit for use with locally generated clock (DCE case)
This is a combination clock and transmit data engine.
It generates the bit clock, which is an output here, along
with the transmit data. It also provides that bit clock
to the receive state machine
out_base is the transmit data pin
out_count is 1
sideset_base is the clock output pin
sideset_count is 1 (with optional side set)
state machine clock: 4x the required bit rate.
output fifo threshold is 8 or 32, joined output fifos
Initialization:
Before starting this state machine, use the execute register to
force the execution of these two instructions:
pull
mov x, osr
The data pushed into the output FIFO by the controlling program is
the sync pattern, 0x96969696.
*/
.program local_clk_xmit
.side_set 1 opt
.wrap_target
irq rxbit side 1 [1]
out pins 1 side 0
pull ifempty noblock
.wrap
/* Transmit, integral modem case
This generates the transmit data stream modulated in integral
modem form.
out_base is the transmit data pin. side_set also assigned this.
out_count is 1
state machine clock: 4x the required bit rate.
output fifo threshold is 8 or 32, joined output fifos
Initialization:
Before starting this state machine, use the execute register to
force the execution of these two instructions:
pull
mov x, osr
The data pushed into the output FIFO by the controlling program is
the sync pattern, 0x96969696.
*/
.program inmodem_xmit
.side_set 1 opt
.wrap_target
loop1: pull ifempty noblock side 1 // start of bit cell, rising edge
out y, 1
mov pins, y // if 0, falling edge
jmp !y, loop1 // stay in this block if zero
loop2: pull ifempty noblock side 0 // start of bit cell, falling edge
out y, 1
mov pins, !y // if 0, rising edge
jmp !y, loop2 // stay in this block if zero
.wrap
/* Transmit, integral modem case, raw waveforms
This sends the transmit data stream supplied by the higher
layer code (no modulation function). When no data is supplied,
the transmit pin idles in whatever state it was last set to.
out_base is the transmit data pin. side_set also assigned this.
out_count is 1
state machine clock: 4x the required data rate (so the state machine
runs in 2 cycles because there are two bauds per bit)
output fifo threshold is 8 or 32, joined output fifos
*/
.program inmodem_rawxmit
.side_set 1 opt
.wrap_target
loop: pull ifempty // get data if needed
out pins 1
.wrap
/* Sync search and data receive
This is the receive byte framer, local clock or integral modem.
The bit clock used here is the one generated by the local
transmit or recovered by the integral modem receive bit recovery.
Apart from waiting for an irq rather than a falling edge on the
clock pin, this code is identical to that of modem_clk_recv above.
in_base is the receive data pin
sideset_base is the "in sync" LED pin
sideset_count is 1 (optional side set)
state machine clock: fast (minimally 8x the bit rate)
input fifo threshold is 32
Initialization:
Before starting this state machine, use the execute register to
force the execution of these three instructions:
pull
mov y, osr
mov isr, null
then set the fifo configuration to joined input fifos. The data
pushed into the output FIFO by the controlling program is the sync
pattern, 0x96969696.
To go back to sync search, jump this state machine back to its
beginning address.
*/
.program local_clk_recv
.side_set 1 opt
search: wait 1 irq rxbit side 0 // indicate not in sync
in pins, 1
mov x, isr
jmp x!=y, search
push side 1 // indicate in sync
.wrap_target
wait 1 irq rxbit
in pins, 1
push iffull noblock
.wrap
/* Integral modem bit recovery
This is the bit clock and data recovery for the integral modem. It
sends the recovered bit stream to a pin which is output here but is
the input pin of the "local_clk_recv" state machine program.
Availability of the recovered bit is signaled by IRQ "rxbit"; this
doesn't happen until the edge that marks the start of the next bit
cell.
The strobe signal (on the next pin) is for test, to allow
observation of the receive eye pattern and the point at which the
state machine strobes the received data. The leading edge
indicates the strobe point. It is also the pin watched by the
frequency measurement state machine for the integral modem case.
in_base is the modulated receive data pin
jmp_pin is that same pin
set_base is the strobe pin
set_count is 1
sideset_base is the recovered data pin (for "local_clk_recv" to use
as input) and strobe pin.
state machine clock is 12x the bit rate.
*/
.program inmodem_rxbit
.side_set 2 opt
wait1: wait 1 pin 0 // wait for rising edge
irq rxbit [2] // signal receive SM
set pins, 0 [3] // drop strobe signal
jmp pin, wait0 side 3 // recovered a 1, strobe
jmp wait1 side 2 // recovered a zero
wait0: wait 0 pin 0 // wait for falling edge
irq rxbit [2] // signal receive SM
set pins, 0 [3] // drop strobe signal
jmp pin, wait0 side 2 // recovered a zero, strobe
jmp wait1 side 3 // recovered a 1
/* Pulse stretcher
This generates a 50 ms pulse on the output pin to produce
an adequately visible flash of light from the connected LED.
To generate a flash, jump this state machine back to its
beginning address. Note that a flash is generated when
this SM first starts, that's used as the device startup
indication.
set_base is the LED pin (e.g., 25 for the built-in LED)
set_count is 1
state machine clock is 2000 Hz.
*/
.program pulse_stretch
set pins, 1 [31] // LED on
nop [31]
.wrap_target
set pins, 0
.wrap
/* Frequency counter
This counts down while watching for 32 periods of the input
pin, and sends the resulting count to the FIFO.
in_base is the clock_ok pin
jmp_base is the pin to monitor
state machine clock is max.
output fifo threshold is 32, joined output fifos.
*/
.program freq
.wrap_target
wait 1 pin 0 // wait for clock ok
set x, 31 // period counter
set y, 0 // tick counter
waitl: jmp y--, wl2 // count a tick
wl2: jmp pin, waitl // loop if not low yet
waith: jmp pin, cycle // exit loop if high
jmp y--, waith // count a tick
cycle: jmp x--, waitl // count cycles
mov isr, y // get the measured tick count
push noblock // send it to the main CPU
.wrap
/* Bit clock watchdog
This watches the bit clock (same pin that the frequency counter
watches), expecting to see a period no greater than N times through
the loop (roughly 2 * N state machine cycles). If the bit clock
stops or slows down more than that, it sets the clock_ok pin to 0.
in_base is the pin to monitor
jmp_base is the pin to monitor
sideset_base is the clock_ok pin
sideset_count is 1
state machine clock is max.
output fifo is used to send a new period value to the watchdog;
this can be used after the frequence measurement SM has measured
the external modem data rate.
Initialization:
Before starting this state machine, use the execute register to
force the execution of these two instructions:
pull
mov x, osr
The value sent is the initial value of the period to use.
*/
.program watchdog
.side_set 1 opt
.wrap_target
top: wait 1 pin 0 side 0 // wait for rising edge, clear clock_ok
wait 0 pin 0 // wait for falling edge
cycle: pull noblock // update x with new period if supplied
mov x, osr // set new x if any
mov y, x
waitl: jmp y--, wl2 // count a tick
jmp top // hit the limit, clear ok, start over
wl2: jmp pin, waitl // loop if not low yet
waith: jmp pin, cycle side 1 // repeat if high, set clock_ok
jmp y--, waith // count a tick
.wrap