TITLE "Serial signal generator / error counter rev.2.02 Jul. 12, 2012";
% Copyright (c) 2006-2012 Finetune co., ltd. and Takayuki HOSODA.   %
%    All rights reserved.                                           %
% Bug reports should be sent to lyuka@finetune.co.jp                %
%Switches%
%----------------------------------------------------------------%
%SW-push   : ON   : alternate display / increase bit rate%
%%
%DIPSW-1   : TEST  -- ON: Syncronous, OFF: Asyncronous(RS232C)%
%DIPSW-2   : NONE  -- ON: No parity(RS232C)%
%DIPSW-3   : ODD   -- ON: Odd parity(RS232C)%
%DIPSW-4   : TXNEG -- ON: Logically invert the output(TxD) signal%
%DIPSW-5   : RXNEG -- ON: Logically invert the input (RxD) signal%
%DIPSW-6   : CRLF  -- ON: LT = CRLF, OFF: LT = LF%
%DIPSW-7   : HFC   -- ON: Enable hardware flow controll (i.e. CTS)%
%%
%SW-toggle : TXON  -- 0N: Activate transmit signal%
%%
%Mnemonic - logic expression        : comment%
%--------------------------------------------------------------------------%
%7 E      - !test & !none & !odd    : RS232C 7bit evan parity 0x20-0xfe, LT%
%7 O      - !test & !none &  odd    : RS232C 7bit odd  parity 0x20-0xfe, LT%
%8 n      - !test &  none & !odd    : RS232C 8bit no   parity 0x20-0xfe, LT%
%F F      - !test &  one  &  odd    : RS232C 8bit no   parity 0x00-0xff%
%n 9      -  test & !none & !odd    :  9 stage PN, ITU-T Rec. O.150, O.153%
%1 5      -  test & !none &  odd    : 15 stage PN, ITU-T Rec. O.150, O.151%
%2 3      -  test &  none & !odd    : 23 stage PN, ITU-T Rec. O.150, O.151%
%u n      -  test &  none &  odd    : Alternate (square wave)%
%%
%Decimal points indicator (while TXON)%
%-------------------------------------------------------%
%DP3 , DP2 , DP1 , DP0%
%L->H, L->H, L->X, L->X%
%H->H, H->X, H->L, H->L%
%lit   lit   off   off  : stack to high%
%off   lit   lit   off  : inverted%
%off   off   lit   lit  : stack to low%
%lit   lit   lit   lit  : random error%
%-------------------------------------------------------%
%%
%Decimal points indicator (while diplaying the format)%
%-------------------------------------------------------%
%---   ---   lit   ---  : CR+LF%
%---   ---   ---   lit  : HHC enabled%
%%
%Indicators%
%-------------------------------------------------------%
%TXON : lit : TXON%
%CTS  : lit : clear to send%
%%
%Display and its code%
%-------------------------------------------------------%
%-- code   : 0 1 2 3 4 5 6 7 8 9 a b c d e f%
%-- display: 0 1 2 3 4 5 6 7 8 9 E F u n - (blank)%
%%
%Frequencies%
%-------------------------------------------------------%
%fclk = 14.7456MHz%
%fdiv0 : 7.3728MHz    0%
%fdiv1 : 3.6864MHz    1%
%fdiv2 : 1.8432MHz    2%
%fdiv3 :   921.6kHz   3%
%fdiv4 :   460.8kHz   4%
%fdiv5 :   230.4kHz   5%
%fdiv6 :   115.2kHz   6%
%fdiv7 :    57.6kHz   7%
%fdiv8 :    28.8kHz%
%fdiv9 :    14.4kHz  10%
%bdiv2 :    38.4kHz   8%
%bdiv3 :    19.2kHz   9%
%bdiv4 :     9.6kHz  11%
%bdiv5 :     4.8kHz  12          , for 7-segment LED dynamic scan%
%bdiv6 :     2.4kHz  13          , for 7-segment LED dynamic scan%
%bdiv7 :     1.2kHz  14%
%bdiv8 :      600Hz  15 /  1.67ms%
%bdiv9 :      300Hz     /  3.33ms%
%bdiv10:      150Hz     /  6.67ms%
%bdiv11:       75Hz     / 13.33ms , for key scan (kstb)%
%ccnt0 :                / 26.67ms%
%ccnt1 :                / 53.33ms%
%ccnt2 :                /106.67ms%
%ccnt3 :                /213.33ms%
%ccnt4 :                /426.66ms, for key hold%
%ccnt5 :                >426.66ms, for key hold%
SUBDESIGN test232c
(
    fclk        : INPUT;  -- gclk1 14.7456MHz
    _sw         : INPUT;  -- push switch to change bit rate
    _test       : INPUT;  -- 0: select PN9, 1: RS232C
    _none       : INPUT;  -- 0: 8bit without parity, 1: 7bit with parity
    _odd        : INPUT;  -- 0: odd parity, 1: even parity
    _txinv      : INPUT;  -- 0: active=low, 1: active=high
    _rxinv      : INPUT;  -- 0: active=low, 1: active=high
    _crlf       : INPUT;  -- 0: LF, 1:CRLF
    _hfc        : INPUT;  -- 0: enable hardware flow control
    _txon       : INPUT;  -- Tx ON
    _rxd        : INPUT;  -- RxD,
    _cts        : INPUT;  -- CTS, 0: clear to send
    ctsd        : OUTPUT; -- clear to send indicator
    txonb       : OUTPUT; -- Tx ON indicator
    led[7..0]   : OUTPUT; -- baud rate indicator
    _ledd[3..0] : OUTPUT; -- led digit driver
    bclk        : OUTPUT; -- baud rate clock
    rerr        : OUTPUT; -- receive data error
    txdb        : OUTPUT; -- serial data to the line driver
    sync        : OUTPUT; -- start bit
)
VARIABLE
    fdiv[9..0]        : DFF;
    frc               : NODE;
    tdiv[1..0]        : DFFE;
    trc               : NODE;
    bdiv[11..0]       : DFFE;
    bclk              : DFF;
    mclk              : DFF;
    mclkd             : DFF;
    mstb              : NODE;
    kclk              : DFF;
    kstb              : NODE;
    ccnt[5..0]        : DFFE;
    lerr              : JKFFE;
    lerrd             : DFFE;
    herr              : JKFFE;
    herrd             : DFFE;
    lfsr[22..0]       : DFFE;
    digd[3..0]        : NODE;
    digc[3..0]        : NODE;
    digb[3..0]        : NODE;
    diga[3..0]        : NODE;
    ledi[3..0]        : NODE;
    ledd[3..0]        : NODE;
    _ledd[3..0]       : OPNDRN;
    rerr              : DFFE;
    errd[3..0]        : DFFE;
    errc[3..0]        : DFFE;
    errb[3..0]        : DFFE;
    erra[3..0]        : DFFE;
    clrerr            : DFF;
    overflow          : NODE;
    ctsb              : DFFE;
    ctsd              : JKFFE;
    sw[2..0]          : DFFE;
    swd               : JKFFE;
    swdd              : DFFE;
    swtx[2..0]        : DFFE;
    txon              : JKFFE;
    selsw[3..0]       : DFFE;
    select[3..0]      : DFFE;
    stopbit           : NODE;
    sync              : DFFE;
    ld                : NODE;
    txdata[7..0]      : DFFE;
    txd               : DFFE;
    test              : DFFE;
    parity            : NODE;
    none              : DFFE;
    odd               : DFFE;
    txinv             : DFFE;
    rxinv             : DFFE;
    crlf              : DFFE;
    hfc               : DFFE;
    mode_c8n          : NODE;
    mode_cff          : NODE;
    mode_c7e          : NODE;
    mode_c7o          : NODE;
    mode_n9           : NODE;
    mode_n15          : NODE;
    mode_n23          : NODE;
    mode_pb           : NODE;
    mode[7..0]        : NODE;
    disp_bps          : NODE;
    disp_err          : NODE;
    disp_info         : NODE;
    tstate : MACHINE OF BITS (txcount[3..0])
             WITH STATES (stop  = 0,
                          start = 1,
                          bit0  = 2,
                          bit1  = 3,
                          bit2  = 4,
                          bit3  = 5,
                          bit4  = 6,
                          bit5  = 7,
                          bit6  = 8,
                          bit7  = 9);
BEGIN
    fdiv[9..0].clk   = GLOBAL(fclk);
    fdiv[9..0] = fdiv[9..0] + 1;
    frc = (fdiv[3..0] == h"f");
    tdiv[1..0].clk   = GLOBAL(fclk);
    tdiv[1..0].ena = frc;
    tdiv1 = tdiv0;
    tdiv0 = !tdiv1 # !tdiv0;
    trc   = ((tdiv[1..0] == 3) & frc);
    bdiv[11..0].clk   = GLOBAL(fclk);
    bdiv[11..0].ena   = trc;
    bdiv[11..0] = (bdiv[11..0] + 1);
    kclk.clk = GLOBAL(fclk);
    kclk = bdiv11;
    kstb = (bdiv11 & !kclk);  -- 13.3ms interval
    sw[2..0].clk = GLOBAL(fclk);
    sw[2..0].ena = kstb;
    --sw[2..0] = (!txon & sw[1..0], !_sw) & kstb;
    sw[2..0] = (sw[1..0], !_sw) & kstb;
    swd.clk      = GLOBAL(fclk);
    swd.ena      = GLOBAL(mstb);
    swd.j = (sw[2..0] == b"111"); -- 40ms period
    swd.k = (sw[2..0] == b"000");
    swdd.clk     = GLOBAL(fclk);
    swdd = swd;
    ccnt[5..0].clk = GLOBAL(fclk);
    ccnt[5..0].ena = kstb;
    ccnt[5..0] =   (ccnt[5..0] + 1) & swd & !ccnt5
                 # (ccnt[5..0]    ) & swd &  ccnt5;
    selsw[3..0].clk   = GLOBAL(fclk);
    selsw[3..0].ena   = !swd & swdd & !ccnt5;
    selsw[3..0] = (selsw[3..0] + 1);
    select[3..0].clk   = GLOBAL(fclk);
    select[3..0].ena   = stopbit & mstb;
    select[3..0] = selsw[3..0];
    swtx[2..0].clk = GLOBAL(fclk);
    swtx[2..0].ena = kstb;
    swtx[2..0] = (swtx[1..0], !_txon);
    txon.clk = GLOBAL(fclk);
    txon.ena = GLOBAL(mstb);
    txon.j = (swtx[2..0] == b"111"); -- 40ms period
    txon.k = (swtx[2..0] == b"000");
    txonb = txon;
    (ctsb, ctsd, hfc).clk = GLOBAL(fclk);
    (ctsb, ctsd, hfc).ena = GLOBAL(mstb);
    hfc = !_hfc;
    ctsb = (!_cts $ rxinv) # (!hfc);
    ctsd.j = ctsb;
    ctsd.k = !ctsb & kstb;
    (none, odd, txinv, rxinv, test, crlf).clk = GLOBAL(fclk);
    (none, odd, txinv, rxinv, test, crlf).ena =   (stopbit & mstb & !test)
                                                # (          kstb &  test);
    test  = !_test;
    none  = !_none;
    odd   = !_odd;
    txinv = !_txinv;
    rxinv = !_rxinv;
    crlf  = !_crlf;
    disp_bps  = (!sw0 & !txon        )
               #( sw0 & !ccnt5       );
    disp_info = ( sw0 &  ccnt5       );
    disp_err  = (!sw0 &  txon);
    mode_c7e = ( !test & !none & !odd );
    mode_c7o = ( !test & !none &  odd );
    mode_c8n = ( !test &  none & !odd );
    mode_cff = ( !test &  none &  odd );
    mode_n9 =  (  test & !none & !odd );
    mode_n15 = (  test & !none &  odd );
    mode_n23 = (  test &  none & !odd );
    mode_pb  = (  test &  none &  odd );
    mode[7..0] = ( h"7a" & mode_c7e
                 # h"70" & mode_c7o
                 # h"8d" & mode_c8n
                 # h"bb" & mode_cff
                 # h"d9" & mode_n9
                 # h"15" & mode_n15
                 # h"23" & mode_n23
                 # h"cd" & mode_pb );
    digd[3..0] = ((h"7" & (select[3..0] == 15))
                 #(h"3" & (select[3..0] == 14))
                 #(h"1" & (select[3..0] == 13))
                 #(h"9" & (select[3..0] == 12))
                 #(h"4" & (select[3..0] == 11))
                 #(h"2" & (select[3..0] == 10))
                 #(h"1" & (select[3..0] ==  9))
                 #(h"f" & (select[3..0] ==  8))
                 #(h"f" & (select[3..0] ==  7))
                 #(h"f" & (select[3..0] ==  6))
                 #(h"f" & (select[3..0] ==  5))
                 #(h"9" & (select[3..0] ==  4))
                 #(h"4" & (select[3..0] ==  3))
                 #(h"2" & (select[3..0] ==  2))
                 #(h"1" & (select[3..0] ==  1))
                 #(h"f" & (select[3..0] ==  0)));
    digc[3..0] = ((h"3" & (select[3..0] == 15))
                 #(h"6" & (select[3..0] == 14))
                 #(h"8" & (select[3..0] == 13))
                 #(h"2" & (select[3..0] == 12))
                 #(h"6" & (select[3..0] == 11))
                 #(h"3" & (select[3..0] == 10))
                 #(h"1" & (select[3..0] ==  9))
                 #(h"5" & (select[3..0] ==  8))
                 #(h"3" & (select[3..0] ==  7))
                 #(h"1" & (select[3..0] ==  6))
                 #(h"1" & (select[3..0] ==  5))
                 #(h"6" & (select[3..0] ==  4))
                 #(h"8" & (select[3..0] ==  3))
                 #(h"4" & (select[3..0] ==  2))
                 #(h"2" & (select[3..0] ==  1))
                 #(h"6" & (select[3..0] ==  0)));
    digb[3..0] = ((h"7" & (select[3..0] == 15))
                 #(h"8" & (select[3..0] == 14))
                 #(h"4" & (select[3..0] == 13))
                 #(h"1" & (select[3..0] == 12))
                 #(h"0" & (select[3..0] == 11))
                 #(h"0" & (select[3..0] == 10))
                 #(h"5" & (select[3..0] ==  9))
                 #(h"7" & (select[3..0] ==  8))
                 #(h"8" & (select[3..0] ==  7))
                 #(h"9" & (select[3..0] ==  6))
                 #(h"4" & (select[3..0] ==  5))
                 #(h"0" & (select[3..0] ==  4))
                 #(h"0" & (select[3..0] ==  3))
                 #(h"0" & (select[3..0] ==  2))
                 #(h"0" & (select[3..0] ==  1))
                 #(h"0" & (select[3..0] ==  0)));
    diga[3..0] = ((h"3" & (select[3..0] == 15))
                 #(h"6" & (select[3..0] == 14))
                 #(h"3" & (select[3..0] == 13))
                 #(h"6" & (select[3..0] == 12))
                 #(h"8" & (select[3..0] == 11))
                 #(h"4" & (select[3..0] == 10))
                 #(h"2" & (select[3..0] ==  9))
                 #(h"6" & (select[3..0] ==  8))
                 #(h"4" & (select[3..0] ==  7))
                 #(h"2" & (select[3..0] ==  6))
                 #(h"4" & (select[3..0] ==  5))
                 #(h"0" & (select[3..0] ==  4))
                 #(h"0" & (select[3..0] ==  3))
                 #(h"0" & (select[3..0] ==  2))
                 #(h"0" & (select[3..0] ==  1))
                 #(h"0" & (select[3..0] ==  0)));
    ledi[3..0] =
                 (h"f" & !txinv & disp_info & ledd3)
               # (h"e" &  txinv & disp_info & ledd3)
               # (mode[7..4]    & disp_info & ledd2)
               # (mode[3..0]    & disp_info & ledd1)
               # (h"f" & !rxinv & disp_info & ledd0)
               # (h"e" &  rxinv & disp_info & ledd0)
               # (digd[3..0]    & disp_bps  & ledd3)
               # (digc[3..0]    & disp_bps  & ledd2)
               # (digb[3..0]    & disp_bps  & ledd1)
               # (diga[3..0]    & disp_bps  & ledd0)
               # (errd[3..0]    & disp_err  & ledd3)
               # (errc[3..0]    & disp_err  & ledd2)
               # (errb[3..0]    & disp_err  & ledd1)
               # (erra[3..0]    & disp_err  & ledd0);
    led7 = ( ((ledd3 & (select[3..0] >= 13))
             #(ledd1 & (select[3..0] <  13) & (select[3..0] >= 5))
             #(ledd0 & (select[3..0] >= 13))) &  disp_bps  )
           # ((ledd1 &  mode_c7o & crlf       &  disp_info )
             #(ledd1 &  mode_c7e & crlf       &  disp_info )
             #(ledd1 &  mode_c8n & crlf       &  disp_info )
             #(ledd0 &  hfc                   &  disp_info )
             #(ledd3 &  herrd  & !lerrd       &  disp_err  )
             #(ledd2 &  herrd                 &  disp_err  )
             #(ledd1 &            lerrd       &  disp_err  )
             #(ledd0 & !herrd &   lerrd       &  disp_err  )
             );
    led[6..0] =  ((b"0000000" & (ledi[3..0] == 15))
                 #(b"1000000" & (ledi[3..0] == 14))
                 #(b"1010100" & (ledi[3..0] == 13))
                 #(b"0011100" & (ledi[3..0] == 12))
                 #(b"1110001" & (ledi[3..0] == 11))
                 #(b"1111001" & (ledi[3..0] == 10))
                 #(b"1101111" & (ledi[3..0] ==  9))
                 #(b"1111111" & (ledi[3..0] ==  8))
                 #(b"0000111" & (ledi[3..0] ==  7))
                 #(b"1111101" & (ledi[3..0] ==  6))
                 #(b"1101101" & (ledi[3..0] ==  5))
                 #(b"1100110" & (ledi[3..0] ==  4))
                 #(b"1001111" & (ledi[3..0] ==  3))
                 #(b"1011011" & (ledi[3..0] ==  2))
                 #(b"0000110" & (ledi[3..0] ==  1))
                 #(b"0111111" & (ledi[3..0] ==  0)));
    _ledd[3..0] = !ledd[3..0];
    ledd0 = (bdiv[6..5] == b"00");
    ledd1 = (bdiv[6..5] == b"01");
    ledd2 = (bdiv[6..5] == b"10");
    ledd3 = (bdiv[6..5] == b"11");
    mclk.clk = GLOBAL(fclk);
    mclk = ((fdiv0    &  (select[3..0] == 15))
           #(fdiv1    &  (select[3..0] == 14))
           #(fdiv2    &  (select[3..0] == 13))
           #(fdiv3    &  (select[3..0] == 12))
           #(fdiv4    &  (select[3..0] == 11))
           #(fdiv5    &  (select[3..0] == 10))
           #(fdiv6    &  (select[3..0] ==  9))
           #(fdiv7    &  (select[3..0] ==  8))
           #(bdiv2    &  (select[3..0] ==  7))
           #(bdiv3    &  (select[3..0] ==  6))
           #(fdiv9    &  (select[3..0] ==  5))
           #(bdiv4    &  (select[3..0] ==  4))
           #(bdiv5    &  (select[3..0] ==  3))
           #(bdiv6    &  (select[3..0] ==  2))
           #(bdiv7    &  (select[3..0] ==  1))
           #(bdiv8    &  (select[3..0] ==  0))
           );
    mclkd.clk = GLOBAL(fclk);
    mclkd = mclk;
    mstb = (mclk & !mclkd); -- master strobe : bit rate strobe
    lfsr[22..0].clk   = GLOBAL(fclk);
    lfsr[22..0].ena   = GLOBAL(mstb);
    lfsr[22..1] = lfsr[21..0];
    lfsr0 = (((lfsr8  $ lfsr4)  # (lfsr[ 8..0] == 0)) & !none & !odd & txon)
           #(((lfsr14 $ lfsr13) # (lfsr[14..0] == 0)) & !none &  odd & txon)
           #(((lfsr22 $ lfsr17) # (lfsr[22..0] == 0)) &  none & !odd & txon);
    sync.clk = GLOBAL(fclk);
    sync.ena = GLOBAL(mstb);
    sync =  (stopbit                     & !test)
          # (stopbit                     & test &  none &  odd)
          # ((lfsr[8..0]  == h"1ff")     & test & !none & !odd)
          # ((lfsr[14..0] == h"7fff")    & test & !none &  odd)
          # ((lfsr[22..0] == h"7fffff")  & test &  none & !odd);
    bclk.clk = GLOBAL(fclk);
    bclk = mclk;
    stopbit = (txcount[3..0] == 0);
    ld = (stopbit & mstb & txon & ctsb);
    txdata[7..0].clk = GLOBAL(fclk);
    txdata[7..0].ena = ld;
    txdata[7..0] =
    (  (txdata[7..0] + 1) & (txdata[7..0] >= h"20") & (txdata[7..0] <  h"7e")
     # (h"20"           ) & (txdata[7..0] <  h"20") & (txdata[7..0] != h"0d")
     # (h"0d"           ) & (txdata[7..0] >= h"7e") &  crlf
     # (h"0a"           ) & (txdata[7..0] >= h"7e") & !crlf
     # (h"0a"           ) & (txdata[7..0] == h"0d")) & (!none # !odd)
    # (txdata[7..0] + 1)  & ( none &  odd);
    parity =   (txdata0 $ txdata1)
             $ (txdata2 $ txdata3)
             $ (txdata4 $  txdata5 $ txdata6);
    txd.clk = GLOBAL(fclk);
    txd.ena = GLOBAL(mstb);
    txd  =
          ((((VCC     & (tstate == stop ))
            #(GND     & (tstate == start))
            #(txdata0 & (tstate == bit0 ))
            #(txdata1 & (tstate == bit1 ))
            #(txdata2 & (tstate == bit2 ))
            #(txdata3 & (tstate == bit3 ))
            #(txdata4 & (tstate == bit4 ))
            #(txdata5 & (tstate == bit5 ))
            #(txdata6 & (tstate == bit6 ))
            #(txdata7 & mode_cff & (tstate == bit7))
            #(!parity & mode_c7o & (tstate == bit7))
            #( parity & mode_c7e & (tstate == bit7))
            ) & !test)
            # (lfsr8  & mode_n9)
            # (lfsr14 & mode_n15)
            # (lfsr22 & mode_n23)
            # (!txd   & mode_pb  & txon ));
    txdb = txinv $ txd;
    clrerr.clk = GLOBAL(fclk);
    clrerr = (!txon # (!swd & swdd & !ccnt5));
    (rerr, lerr, herr).clk = GLOBAL(fclk);
    (rerr, lerr, herr).ena = GLOBAL(mstb);
    rerr = !_rxd $ rxinv $ txd;
    lerr.j =  _rxd & (rxinv $  txd);
    lerr.k = lerrd & ((!_rxd $ rxinv) == txd);
    herr.j = !_rxd & (rxinv $ !txd);
    herr.k = herrd & ((!_rxd $ rxinv) == txd);
    (lerrd, herrd).clk = GLOBAL(fclk);
    (lerrd, herrd).ena = kstb;
    (lerrd, herrd) = (lerr, herr);
    (erra[3..0], errb[3..0], errc[3..0], errd[3..0]).clk = GLOBAL(fclk);
    (erra[3..0], errb[3..0], errc[3..0], errd[3..0]).clrn = !clrerr;
    erra[3..0].ena = mstb & rerr;
    erra[3..0] = (erra[3..0] + 1) & (erra[3..0] < 9);
    errb[3..0].ena =  mstb & rerr & (erra[3..0] == 9);
    errb[3..0] = (errb[3..0] + 1) & (errb[3..0] < 9);
    errc[3..0].ena = mstb & rerr & (errb[3..0] == 9) & (erra[3..0] == 9);
    errc[3..0] = (errc[3..0] + 1) & (errc[3..0] < 9);
    errd[3..0].ena =  mstb & rerr
                    & (errc[3..0] == 9) & (errb[3..0] == 9) & (erra[3..0] == 9);
    overflow = (errd[3..0] >= 10);
    errd[3..0] = (errd[3..0] + 1) & !overflow
                #(errd[3..0]    ) &  overflow;
    tstate.clk = GLOBAL(fclk);
    tstate.ena = GLOBAL(mstb);
    CASE tstate IS
        WHEN stop =>
            IF (txon & ctsb) THEN
                tstate = start;
            ELSE
                tstate = stop;
            END IF;
        WHEN start => tstate = bit0;
        WHEN bit0 => tstate = bit1;
        WHEN bit1 => tstate = bit2;
        WHEN bit2 => tstate = bit3;
        WHEN bit3 => tstate = bit4;
        WHEN bit4 => tstate = bit5;
        WHEN bit5 => tstate = bit6;
        WHEN bit6 => tstate = bit7;
        WHEN bit7 => tstate = stop;
    END CASE;
END;