mirror of
https://github.com/pkoning2/ddcmp.git
synced 2026-01-11 23:53:01 +00:00
2. Make frequency measurement better for integral modem case. 3. New test mode: raw waveform (for integral modem).
337 lines
11 KiB
Plaintext
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
|