Legic Tag Simulator (#666)
authorAntiCat <contiki@anticat.ch>
Sun, 9 Sep 2018 14:40:20 +0000 (16:40 +0200)
committerpwpiwi <pwpiwi@users.noreply.github.com>
Sun, 9 Sep 2018 14:40:20 +0000 (16:40 +0200)
* FPGA Hi-Simulate: Formatted code
* FPGA Hi-Simulate: Fixed documantation
* FPGA Hi-Simulate: Freed up 4 LUTs
* FPGA Hi-Simulate: Added 212kHz SSP-Clock option
* Legic: Moved card simulator into separate file & cleaned interface.
Reader and card simulation have almost no common code. Moreover the sim
uses an SSP Clock at 212kHz for all timings to prevent any drifting from
the PRNG. This clock speed is not available in reader simulation mode (SSP
runs at up to 3.4MHz, and changes speed between TX and RX). For these
reasons having the code in separate files makes it significantly cleaner.
* Legic: Implemented RX and TX for card simulation
* Legic: Implemented setup phase for card simulation
* Legic: Implemented read command for card simulation
* Legic: Implemented write command for card simulation

armsrc/Makefile
armsrc/appmain.c
armsrc/legicrf.c
armsrc/legicrf.h
armsrc/legicrfsim.c [new file with mode: 0644]
armsrc/legicrfsim.h [new file with mode: 0644]
client/cmdhflegic.c
fpga/fpga_hf.bit
fpga/hi_simulate.v

index d4b13c6bf59066a1a122d13237fb2653292fa3d8..046ad1bc17da8df991c2e2ea960bf33e90389cdf 100644 (file)
@@ -59,11 +59,12 @@ THUMBSRC = start.c \
 # These are to be compiled in ARM mode
 ARMSRC = fpgaloader.c \
        legicrf.c \
+       legicrfsim.c \
+       legic_prng.c \
        $(SRC_ISO14443a) \
        $(SRC_ISO14443b) \
        $(SRC_CRAPTO1) \
        $(SRC_CRC) \
-       legic_prng.c \
        iclass.c \
        BigBuf.c \
        optimized_cipher.c \
index 4034788afc5f0260bd3545d3ddf4ab5f8201c50e..f7bcd620348a0b5e90fc0ab1f3f19699d5cd2236 100644 (file)
@@ -21,6 +21,7 @@
 #include "printf.h"
 #include "string.h"
 #include "legicrf.h"
+#include "legicrfsim.h"
 #include "hitag2.h"
 #include "hitagS.h"
 #include "lfsampling.h"
@@ -1090,7 +1091,7 @@ void UsbPacketReceived(uint8_t *packet, int len)
 
 #ifdef WITH_LEGICRF
                case CMD_SIMULATE_TAG_LEGIC_RF:
-                       LegicRfSimulate(c->arg[0], c->arg[1], c->arg[2]);
+                       LegicRfSimulate(c->arg[0]);
                        break;
 
                case CMD_WRITER_LEGIC_RF:
index 2a236b6ff9e04ad0d6373560a4f20aea08ccf5b5..c8a4829f7d51c8b4abf658a9839d9c200211bb56 100644 (file)
@@ -1,7 +1,7 @@
 //-----------------------------------------------------------------------------
 // (c) 2009 Henryk Plötz <henryk@ploetzli.ch>
 //     2016 Iceman
-//     2018 AntiCat (rwd rewritten)
+//     2018 AntiCat
 //
 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
 // at your option, any later version. See the LICENSE.txt file for the text of
 #include "legic.h"
 #include "crc.h"
 
-static struct legic_frame {
-       int bits;
-       uint32_t data;
-} current_frame;
-
-static enum {
-  STATE_DISCON,
-  STATE_IV,
-  STATE_CON,
-} legic_state;
-
-static crc_t    legic_crc;
-static int      legic_read_count;
-static uint32_t legic_prng_bc;
-static uint32_t legic_prng_iv;
-
-static int      legic_phase_drift;
-static int      legic_frame_drift;
-static int      legic_reqresp_drift;
-
-AT91PS_TC timer;
-AT91PS_TC prng_timer;
-
 static legic_card_select_t card;/* metadata of currently selected card */
+static crc_t legic_crc;
 
 //-----------------------------------------------------------------------------
 // Frame timing and pseudorandom number generator
@@ -71,11 +49,6 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */
 #define TAG_BIT_PERIOD      150 /* 100us */
 #define TAG_WRITE_TIMEOUT    60 /* 40 * 100us (write should take at most 3.6ms) */
 
-#define SIM_DIVISOR         586 /* prng_time/DIV count prng needs to be forwared */
-#define SIM_SHIFT           900 /* prng_time+SHIFT shift of delayed start */
-#define RWD_TIME_FUZZ        20 /* rather generous 13us, since the peak detector
-                                /+ hysteresis fuzz quite a bit */
-
 #define LEGIC_READ         0x01 /* Read Command */
 #define LEGIC_WRITE        0x00 /* Write Command */
 
@@ -86,8 +59,6 @@ static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */
 #define INPUT_THRESHOLD       8 /* heuristically determined, lower values */
                                 /* lead to detecting false ack during write */
 
-#define FUZZ_EQUAL(value, target, fuzz) ((value) > ((target)-(fuzz)) && (value) < ((target)+(fuzz)))
-
 //-----------------------------------------------------------------------------
 // I/O interface abstraction (FPGA -> ARM)
 //-----------------------------------------------------------------------------
@@ -125,8 +96,8 @@ static inline uint8_t rx_byte_from_fpga() {
 // also.
 //
 // The demedulated should be alligned to the bit periode by the caller. This is
-// done in rx_bit_as_reader and rx_ack_as_reader.
-static inline bool rx_bit_as_reader() {
+// done in rx_bit and rx_ack.
+static inline bool rx_bit() {
   int32_t cq = 0;
   int32_t ci = 0;
 
@@ -158,7 +129,7 @@ static inline bool rx_bit_as_reader() {
 // be circumvented, but the adventage over bitbang would be little.
 //-----------------------------------------------------------------------------
 
-static inline void tx_bit_as_reader(bool bit) {
+static inline void tx_bit(bool bit) {
   // insert pause
   LOW(GPIO_SSC_DOUT);
   last_frame_end += RWD_TIME_PAUSE;
@@ -180,7 +151,7 @@ static inline void tx_bit_as_reader(bool bit) {
 // present.
 //-----------------------------------------------------------------------------
 
-static void tx_frame_as_reader(uint32_t frame, uint8_t len) {
+static void tx_frame(uint32_t frame, uint8_t len) {
   FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_TX);
 
   // wait for next tx timeslot
@@ -190,7 +161,7 @@ static void tx_frame_as_reader(uint32_t frame, uint8_t len) {
   // transmit frame, MSB first
   for(uint8_t i = 0; i < len; ++i) {
     bool bit = (frame >> i) & 0x01;
-    tx_bit_as_reader(bit ^ legic_prng_get_bit());
+    tx_bit(bit ^ legic_prng_get_bit());
     legic_prng_forward(1);
   };
 
@@ -201,7 +172,7 @@ static void tx_frame_as_reader(uint32_t frame, uint8_t len) {
   HIGH(GPIO_SSC_DOUT);
 }
 
-static uint32_t rx_frame_as_reader(uint8_t len) {
+static uint32_t rx_frame(uint8_t len) {
   FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
                   | FPGA_HF_READER_RX_XCORR_848_KHZ
                   | FPGA_HF_READER_RX_XCORR_QUARTER_FREQ);
@@ -211,11 +182,11 @@ static uint32_t rx_frame_as_reader(uint8_t len) {
   while(GET_TICKS < last_frame_end) { };
 
   uint32_t frame = 0;
-  for(uint8_t i = 0; i < len; i++) {
-    frame |= (rx_bit_as_reader() ^ legic_prng_get_bit()) << i;
+  for(uint8_t i = 0; i < len; ++i) {
+    frame |= (rx_bit() ^ legic_prng_get_bit()) << i;
     legic_prng_forward(1);
 
-    // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD
+    // rx_bit runs only 95us, resync to TAG_BIT_PERIOD
     last_frame_end += TAG_BIT_PERIOD;
     while(GET_TICKS < last_frame_end) { };
   }
@@ -223,7 +194,7 @@ static uint32_t rx_frame_as_reader(uint8_t len) {
   return frame;
 }
 
-static bool rx_ack_as_reader() {
+static bool rx_ack() {
   // change fpga into rx mode
   FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_READER_RX_XCORR
                   | FPGA_HF_READER_RX_XCORR_848_KHZ
@@ -236,10 +207,10 @@ static bool rx_ack_as_reader() {
   uint32_t ack = 0;
   for(uint8_t i = 0; i < TAG_WRITE_TIMEOUT; ++i) {
     // sample bit
-    ack = rx_bit_as_reader();
+    ack = rx_bit();
     legic_prng_forward(1);
 
-    // rx_bit_as_reader runs only 95us, resync to TAG_BIT_PERIOD
+    // rx_bit runs only 95us, resync to TAG_BIT_PERIOD
     last_frame_end += TAG_BIT_PERIOD;
     while(GET_TICKS < last_frame_end) { };
 
@@ -256,7 +227,7 @@ static bool rx_ack_as_reader() {
 // Legic Reader
 //-----------------------------------------------------------------------------
 
-int init_card(uint8_t cardtype, legic_card_select_t *p_card) {
+static int init_card(uint8_t cardtype, legic_card_select_t *p_card) {
   p_card->tagtype = cardtype;
 
   switch(p_card->tagtype) {
@@ -313,8 +284,8 @@ static void init_reader(bool clear_mem) {
 // The setup consists of a three way handshake:
 //  - Transmit initialisation vector 7 bits
 //  - Receive card type 6 bits
-//  - Acknowledge frame 6 bits
-static uint32_t setup_phase_reader(uint8_t iv) {
+//  - Transmit Acknowledge 6 bits
+static uint32_t setup_phase(uint8_t iv) {
   // init coordination timestamp
   last_frame_end = GET_TICKS;
 
@@ -323,24 +294,24 @@ static uint32_t setup_phase_reader(uint8_t iv) {
   while(GET_TICKS < last_frame_end) { };
 
   legic_prng_init(0);
-  tx_frame_as_reader(iv, 7);
+  tx_frame(iv, 7);
 
-  // configure iv
+  // configure prng
   legic_prng_init(iv);
   legic_prng_forward(2);
 
   // receive card type
-  int32_t card_type = rx_frame_as_reader(6);
+  int32_t card_type = rx_frame(6);
   legic_prng_forward(3);
 
   // send obsfuscated acknowledgment frame
   switch (card_type) {
     case 0x0D:
-      tx_frame_as_reader(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01
+      tx_frame(0x19, 6); // MIM22 | READCMD = 0x18 | 0x01
       break;
     case 0x1D:
     case 0x3D:
-      tx_frame_as_reader(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01
+      tx_frame(0x39, 6); // MIM256 | READCMD = 0x38 | 0x01
       break;
   }
 
@@ -359,9 +330,9 @@ static int16_t read_byte(uint16_t index, uint8_t cmd_sz) {
   // read one byte
   LED_B_ON();
   legic_prng_forward(2);
-  tx_frame_as_reader(cmd, cmd_sz);
+  tx_frame(cmd, cmd_sz);
   legic_prng_forward(2);
-  uint32_t frame = rx_frame_as_reader(12);
+  uint32_t frame = rx_frame(12);
   LED_B_OFF();
 
   // split frame into data and crc
@@ -391,12 +362,12 @@ bool write_byte(uint16_t index, uint8_t byte, uint8_t addr_sz) {
   // send write command
   LED_C_ON();
   legic_prng_forward(2);
-  tx_frame_as_reader(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc
+  tx_frame(cmd, addr_sz + 1 + 8 + 4); // sz = addr_sz + cmd + data + crc
   legic_prng_forward(3);
   LED_C_OFF();
 
   // wait for ack
-  return rx_ack_as_reader();
+  return rx_ack();
 }
 
 //-----------------------------------------------------------------------------
@@ -413,7 +384,7 @@ void LegicRfReader(int offset, int bytes) {
 
   // establish shared secret and detect card type
   DbpString("Reading card ...");
-  uint8_t card_type = setup_phase_reader(SESSION_IV);
+  uint8_t card_type = setup_phase(SESSION_IV);
   if(init_card(card_type, &card) != 0) {
     Dbprintf("No or unknown card found, aborting");
     goto OUT;
@@ -463,7 +434,7 @@ void LegicRfWriter(int bytes, int offset) {
 
   // establish shared secret and detect card type
   Dbprintf("Writing 0x%02.2x - 0x%02.2x ...", offset, offset+bytes);
-  uint8_t card_type = setup_phase_reader(SESSION_IV);
+  uint8_t card_type = setup_phase(SESSION_IV);
   if(init_card(card_type, &card) != 0) {
     Dbprintf("No or unknown card found, aborting");
     goto OUT;
@@ -492,345 +463,3 @@ OUT:
   LED_D_OFF();
   StopTicks();
 }
-
-//-----------------------------------------------------------------------------
-// Legic Simulator
-//-----------------------------------------------------------------------------
-
-static void setup_timer(void)
-{
-       /* Set up Timer 1 to use for measuring time between pulses. Since we're bit-banging
-        * this it won't be terribly accurate but should be good enough.
-        */
-       AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1);
-       timer = AT91C_BASE_TC1;
-       timer->TC_CCR = AT91C_TC_CLKDIS;
-       timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
-       timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
-
-       /* 
-     * Set up Timer 2 to use for measuring time between frames in 
-     * tag simulation mode. Runs 4x faster as Timer 1
-        */
-    AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2);
-    prng_timer = AT91C_BASE_TC2;
-    prng_timer->TC_CCR = AT91C_TC_CLKDIS;
-       prng_timer->TC_CMR = AT91C_TC_CLKS_TIMER_DIV2_CLOCK;
-    prng_timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
-}
-
-/* Generate Keystream */
-static uint32_t get_key_stream(int skip, int count)
-{
-  uint32_t key=0; int i;
-
-  /* Use int to enlarge timer tc to 32bit */
-  legic_prng_bc += prng_timer->TC_CV;
-  prng_timer->TC_CCR = AT91C_TC_SWTRG;
-
-  /* If skip == -1, forward prng time based */
-  if(skip == -1) {
-     i  = (legic_prng_bc+SIM_SHIFT)/SIM_DIVISOR; /* Calculate Cycles based on timer */
-     i -= legic_prng_count(); /* substract cycles of finished frames */
-     i -= count; /* substract current frame length, rewidn to bedinning */
-     legic_prng_forward(i);
-  } else {
-     legic_prng_forward(skip);
-  }
-
-  /* Write Time Data into LOG */
-  uint8_t *BigBuf = BigBuf_get_addr();
-  if(count == 6) { i = -1; } else { i = legic_read_count; }
-  BigBuf[OFFSET_LOG+128+i] = legic_prng_count();
-  BigBuf[OFFSET_LOG+256+i*4]   = (legic_prng_bc >> 0) & 0xff;
-  BigBuf[OFFSET_LOG+256+i*4+1] = (legic_prng_bc >> 8) & 0xff;
-  BigBuf[OFFSET_LOG+256+i*4+2] = (legic_prng_bc >>16) & 0xff;
-  BigBuf[OFFSET_LOG+256+i*4+3] = (legic_prng_bc >>24) & 0xff;
-  BigBuf[OFFSET_LOG+384+i] = count;
-
-  /* Generate KeyStream */
-  for(i=0; i<count; i++) {
-    key |= legic_prng_get_bit() << i;
-    legic_prng_forward(1);
-  }
-  return key;
-}
-
-/* Send a frame in tag mode, the FPGA must have been set up by
- * LegicRfSimulate
- */
-static void frame_send_tag(uint16_t response, int bits, int crypt)
-{
-   /* Bitbang the response */
-   AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
-   AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
-   AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
-        
-   /* Use time to crypt frame */
-   if(crypt) {
-      legic_prng_forward(2); /* TAG_TIME_WAIT -> shift by 2 */
-      int i; int key = 0;
-      for(i=0; i<bits; i++) {
-         key |= legic_prng_get_bit() << i;
-         legic_prng_forward(1);
-      }
-      //Dbprintf("key = 0x%x", key);
-      response = response ^ key;
-   }
-
-   /* Wait for the frame start */
-   while(timer->TC_CV < (TAG_FRAME_WAIT - 30)) ;
-       
-   int i;
-   for(i=0; i<bits; i++) {
-      int nextbit = timer->TC_CV + TAG_BIT_PERIOD;
-      int bit = response & 1;
-      response = response >> 1;
-      if(bit) {
-         AT91C_BASE_PIOA->PIO_SODR = GPIO_SSC_DOUT;
-      } else {
-         AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
-      }
-      while(timer->TC_CV < nextbit) ;
-   }
-   AT91C_BASE_PIOA->PIO_CODR = GPIO_SSC_DOUT;
-}
-
-static void frame_append_bit(struct legic_frame * const f, int bit)
-{
-   if(f->bits >= 31) {
-       return; /* Overflow, won't happen */
-   }
-   f->data |= (bit<<f->bits);
-   f->bits++;
-}
-
-static void frame_clean(struct legic_frame * const f)
-{
-       f->data = 0;
-       f->bits = 0;
-}
-
-/* Handle (whether to respond) a frame in tag mode */
-static void frame_handle_tag(struct legic_frame const * const f)
-{
-       uint8_t *BigBuf = BigBuf_get_addr();
-
-   /* First Part of Handshake (IV) */
-   if(f->bits == 7) {
-     if(f->data == SESSION_IV) {
-        LED_C_ON();
-        prng_timer->TC_CCR = AT91C_TC_SWTRG;
-        legic_prng_init(f->data);
-        frame_send_tag(0x3d, 6, 1); /* 0x3d^0x26 = 0x1b */
-        legic_state = STATE_IV;
-        legic_read_count = 0;
-        legic_prng_bc = 0;
-        legic_prng_iv = f->data;
-        /* TIMEOUT */
-        timer->TC_CCR = AT91C_TC_SWTRG;
-        while(timer->TC_CV > 1);
-        while(timer->TC_CV < 280);
-        return;
-      } else if((prng_timer->TC_CV % 50) > 40) {
-        legic_prng_init(f->data);
-        frame_send_tag(0x3d, 6, 1);
-        SpinDelay(20);
-        return;
-     }
-   }
-
-   /* 0x19==??? */
-   if(legic_state == STATE_IV) {
-      if((f->bits == 6) && (f->data == (0x19 ^ get_key_stream(1, 6)))) {
-         legic_state = STATE_CON;
-
-         /* TIMEOUT */
-         timer->TC_CCR = AT91C_TC_SWTRG;
-         while(timer->TC_CV > 1);
-         while(timer->TC_CV < 200);
-         return;
-      } else {
-         legic_state = STATE_DISCON;
-         LED_C_OFF();
-         Dbprintf("0x19 - Frame: %03.3x", f->data);
-         return;
-      }
-   }
-
-   /* Read */
-   if(f->bits == 11) {
-      if(legic_state == STATE_CON) {
-         int key   = get_key_stream(-1, 11); //legic_phase_drift, 11);
-         int addr  = f->data ^ key; addr = addr >> 1;
-         int data = BigBuf[addr];
-         int hash = calc_crc4(addr, data, 11) << 8;
-         BigBuf[OFFSET_LOG+legic_read_count] = (uint8_t)addr;
-         legic_read_count++;
-
-         //Dbprintf("Data:%03.3x, key:%03.3x, addr: %03.3x, read_c:%u", f->data, key, addr, read_c);
-         legic_prng_forward(legic_reqresp_drift);
-
-         frame_send_tag(hash | data, 12, 1);
-
-         /* SHORT TIMEOUT */
-         timer->TC_CCR = AT91C_TC_SWTRG;
-         while(timer->TC_CV > 1);
-         legic_prng_forward(legic_frame_drift);
-         while(timer->TC_CV < 180);
-         return;
-      }
-   }
-
-   /* Write */
-   if(f->bits == 23) {
-      int key   = get_key_stream(-1, 23); //legic_frame_drift, 23);
-      int addr  = f->data ^ key; addr = addr >> 1; addr = addr & 0x3ff;
-      int data  = f->data ^ key; data = data >> 11; data = data & 0xff;
-
-      /* write command */
-      legic_state = STATE_DISCON;
-      LED_C_OFF();
-      Dbprintf("write - addr: %x, data: %x", addr, data);
-      return;
-   }
-
-   if(legic_state != STATE_DISCON) {
-      Dbprintf("Unexpected: sz:%u, Data:%03.3x, State:%u, Count:%u", f->bits, f->data, legic_state, legic_read_count);
-      int i;
-      Dbprintf("IV: %03.3x", legic_prng_iv);
-      for(i = 0; i<legic_read_count; i++) {
-         Dbprintf("Read Nb: %u, Addr: %u", i, BigBuf[OFFSET_LOG+i]);
-      }
-
-      for(i = -1; i<legic_read_count; i++) {
-         uint32_t t;
-         t  = BigBuf[OFFSET_LOG+256+i*4];
-         t |= BigBuf[OFFSET_LOG+256+i*4+1] << 8;
-         t |= BigBuf[OFFSET_LOG+256+i*4+2] <<16;
-         t |= BigBuf[OFFSET_LOG+256+i*4+3] <<24;
-
-         Dbprintf("Cycles: %u, Frame Length: %u, Time: %u", 
-            BigBuf[OFFSET_LOG+128+i],
-            BigBuf[OFFSET_LOG+384+i],
-            t);
-      }
-   }
-   legic_state = STATE_DISCON; 
-   legic_read_count = 0;
-   SpinDelay(10);
-   LED_C_OFF();
-   return; 
-}
-
-/* Read bit by bit untill full frame is received
- * Call to process frame end answer
- */
-static void emit(int bit)
-{
-  if(bit == -1) {
-     if(current_frame.bits <= 4) {
-        frame_clean(&current_frame);
-     } else {
-        frame_handle_tag(&current_frame);
-        frame_clean(&current_frame);
-     }
-     WDT_HIT();
-  } else if(bit == 0) {
-    frame_append_bit(&current_frame, 0);
-  } else if(bit == 1) {
-    frame_append_bit(&current_frame, 1);
-  }
-}
-
-void LegicRfSimulate(int phase, int frame, int reqresp)
-{
-  /* ADC path high-frequency peak detector, FPGA in high-frequency simulator mode, 
-   * modulation mode set to 212kHz subcarrier. We are getting the incoming raw
-   * envelope waveform on DIN and should send our response on DOUT.
-   *
-   * The LEGIC RF protocol is pulse-pause-encoding from reader to card, so we'll
-   * measure the time between two rising edges on DIN, and no encoding on the
-   * subcarrier from card to reader, so we'll just shift out our verbatim data
-   * on DOUT, 1 bit is 100us. The time from reader to card frame is still unclear,
-   * seems to be 300us-ish.
-   */
-
-   if(phase < 0) {
-      int i;
-      for(i=0; i<=reqresp; i++) {
-         legic_prng_init(SESSION_IV);
-         Dbprintf("i=%u, key 0x%3.3x", i, get_key_stream(i, frame));
-      }
-      return;
-   }
-
-   legic_phase_drift = phase;
-   legic_frame_drift = frame;
-   legic_reqresp_drift = reqresp;
-
-   FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
-   SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
-   FpgaSetupSsc();
-   FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR | FPGA_HF_SIMULATOR_MODULATE_212K);
-   
-   /* Bitbang the receiver */
-   AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_DIN;
-   AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DIN;
-   
-   setup_timer();
-   crc_init(&legic_crc, 4, 0x19 >> 1, 0x5, 0);
-   
-   int old_level = 0;
-   int active = 0;
-   legic_state = STATE_DISCON;
-
-   LED_B_ON();
-   DbpString("Starting Legic emulator, press button to end");
-   while(!BUTTON_PRESS()) {
-      int level = !!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN);
-      int time = timer->TC_CV;
-                
-      if(level != old_level) {
-         if(level == 1) {
-            timer->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
-            if(FUZZ_EQUAL(time, RWD_TIME_1, RWD_TIME_FUZZ)) {
-               /* 1 bit */
-               emit(1);
-               active = 1;
-               LED_A_ON();
-            } else if(FUZZ_EQUAL(time, RWD_TIME_0, RWD_TIME_FUZZ)) {
-               /* 0 bit */
-               emit(0);
-               active = 1;
-               LED_A_ON();
-            } else if(active) {
-               /* invalid */
-               emit(-1);
-               active = 0;
-               LED_A_OFF();
-            }
-         }
-      }
-
-      if(time >= (RWD_TIME_1+RWD_TIME_FUZZ) && active) {
-         /* Frame end */
-         emit(-1);
-         active = 0;
-         LED_A_OFF();
-      }
-                
-      if(time >= (20*RWD_TIME_1) && (timer->TC_SR & AT91C_TC_CLKSTA)) {
-         timer->TC_CCR = AT91C_TC_CLKDIS;
-      }
-                
-      old_level = level;
-      WDT_HIT();
-   }
-   DbpString("Stopped");
-   LED_B_OFF();
-   LED_A_OFF();
-   LED_C_OFF();
-}
-
index 464598566b4b1546b0e89de8cde033c338b4ea80..9f8cf457eee3554de170ab9765687eb01b251960 100644 (file)
@@ -1,5 +1,6 @@
 //-----------------------------------------------------------------------------
 // (c) 2009 Henryk Plötz <henryk@ploetzli.ch>
+//     2018 AntiCat
 //
 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
 // at your option, any later version. See the LICENSE.txt file for the text of
@@ -11,7 +12,6 @@
 #ifndef __LEGICRF_H
 #define __LEGICRF_H
 
-extern void LegicRfSimulate(int phase, int frame, int reqresp);
 extern void LegicRfReader(int bytes, int offset);
 extern void LegicRfWriter(int bytes, int offset);
 
diff --git a/armsrc/legicrfsim.c b/armsrc/legicrfsim.c
new file mode 100644 (file)
index 0000000..07a0a62
--- /dev/null
@@ -0,0 +1,468 @@
+//-----------------------------------------------------------------------------
+// (c) 2009 Henryk Plötz <henryk@ploetzli.ch>
+//     2016 Iceman
+//     2018 AntiCat
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// LEGIC RF simulation code
+//-----------------------------------------------------------------------------
+
+#include "proxmark3.h"
+#include "apps.h"
+#include "util.h"
+#include "string.h"
+
+#include "legicrfsim.h"
+#include "legic_prng.h"
+#include "legic.h"
+#include "crc.h"
+
+static uint8_t* legic_mem;      /* card memory, used for sim */
+static legic_card_select_t card;/* metadata of currently selected card */
+static crc_t legic_crc;
+
+//-----------------------------------------------------------------------------
+// Frame timing and pseudorandom number generator
+//
+// The Prng is forwarded every 99.1us (TAG_BIT_PERIOD), except when the reader is
+// transmitting. In that case the prng has to be forwarded every bit transmitted:
+//  - 31.3us for a 0 (RWD_TIME_0)
+//  - 99.1us for a 1 (RWD_TIME_1)
+//
+// The data dependent timing makes writing comprehensible code significantly
+// harder. The current aproach forwards the prng data based if there is data on
+// air and time based, using GetCountSspClk(), during computational and wait
+// periodes. SSP Clock is clocked by the FPGA at 212 kHz (subcarrier frequency).
+//
+// To not have the necessity to calculate/guess exection time dependend timeouts
+// tx_frame and rx_frame use a shared timestamp to coordinate tx and rx timeslots.
+//-----------------------------------------------------------------------------
+
+static uint32_t last_frame_end; /* ts of last bit of previews rx or tx frame */
+
+#define TAG_FRAME_WAIT       70 /* 330us from READER frame end to TAG frame start */
+#define TAG_ACK_WAIT        758 /* 3.57ms from READER frame end to TAG write ACK */
+#define TAG_BIT_PERIOD       21 /* 99.1us */
+
+#define RWD_TIME_PAUSE        4 /* 18.9us */
+#define RWD_TIME_1           21 /* RWD_TIME_PAUSE 18.9us off + 80.2us on = 99.1us */
+#define RWD_TIME_0           13 /* RWD_TIME_PAUSE 18.9us off + 42.4us on = 61.3us */
+#define RWD_CMD_TIMEOUT      40 /* 40 * 99.1us (arbitrary value) */
+#define RWD_MIN_FRAME_LEN     6 /* Shortest frame is 6 bits */
+#define RWD_MAX_FRAME_LEN    23 /* Longest frame is 23 bits */
+
+#define RWD_PULSE             1 /* Pulse is signaled with GPIO_SSC_DIN high */
+#define RWD_PAUSE             0 /* Pause is signaled with GPIO_SSC_DIN low */
+
+//-----------------------------------------------------------------------------
+// Demodulation
+//-----------------------------------------------------------------------------
+
+// Returns true if a pulse/pause is received within timeout
+static inline bool wait_for(bool value, const uint32_t timeout) {
+  while((bool)(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_DIN) != value) {
+    if(GetCountSspClk() > timeout) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Returns a demedulated bit or -1 on code violation
+//
+// rx_bit decodes bits using a thresholds. rx_bit has to be called by as soon as
+// a frame starts (first pause is received). rx_bit checks for a pause up to
+// 18.9us followed by a pulse of 80.2us or 42.4us:
+//  - A bit length <18.9us is a code violation
+//  - A bit length >80.2us is a 1
+//  - A bit length <80.2us is a 0
+//  - A bit length >148.6us is a code violation
+static inline int8_t rx_bit() {
+  // backup ts for threshold calculation
+  uint32_t bit_start = last_frame_end;
+
+  // wait for pause to end
+  if(!wait_for(RWD_PULSE, bit_start + RWD_TIME_1*3/2)) {
+    return -1;
+  }
+
+  // wait for next pause
+  if(!wait_for(RWD_PAUSE, bit_start + RWD_TIME_1*3/2)) {
+    return -1;
+  }
+
+  // update bit and frame end
+  last_frame_end = GetCountSspClk();
+
+  // check for code violation (bit to short)
+  if(last_frame_end - bit_start < RWD_TIME_PAUSE) {
+    return -1;
+  }
+
+  // apply threshold (average of RWD_TIME_0 and )
+  return (last_frame_end - bit_start > (RWD_TIME_0 + RWD_TIME_1) / 2);
+}
+
+//-----------------------------------------------------------------------------
+// Modulation
+//
+// LEGIC RF uses a very basic load modulation from card to reader:
+//  - Subcarrier on for a 1
+//  - Subcarrier off for for a 0
+//
+// The 212kHz subcarrier is generated by the FPGA as well as a mathcing ssp clk.
+// Each bit is transfered in a 99.1us slot and the first timeslot starts 330us
+// after the final 20us pause generated by the reader.
+//-----------------------------------------------------------------------------
+
+// Transmits a bit
+//
+// Note: The Subcarrier is not disabled during bits to prevent glitches. This is
+//       not mandatory but results in a cleaner signal. tx_frame will disable
+//       the subcarrier when the frame is done.
+static inline void tx_bit(bool bit) {
+  LED_C_ON();
+
+  if(bit) {
+    // modulate subcarrier
+    HIGH(GPIO_SSC_DOUT);
+  } else {
+    // do not modulate subcarrier
+    LOW(GPIO_SSC_DOUT);
+  }
+
+  // wait for tx timeslot to end
+  last_frame_end += TAG_BIT_PERIOD;
+  while(GetCountSspClk() < last_frame_end) { };
+  LED_C_OFF();
+}
+
+//-----------------------------------------------------------------------------
+// Frame Handling
+//
+// The LEGIC RF protocol from reader to card does not include explicit frame
+// start/stop information or length information. The tag detects end of frame
+// trough an extended pulse (>99.1us) without a pause.
+// In reverse direction (card to reader) the number of bites is well known
+// and depends only the command received (IV, ACK, READ or WRITE).
+//-----------------------------------------------------------------------------
+
+static void tx_frame(uint32_t frame, uint8_t len) {
+  // wait for next tx timeslot
+  last_frame_end += TAG_FRAME_WAIT;
+  legic_prng_forward(TAG_FRAME_WAIT/TAG_BIT_PERIOD - 1);
+  while(GetCountSspClk() < last_frame_end) { };
+
+  // transmit frame, MSB first
+  for(uint8_t i = 0; i < len; ++i) {
+    bool bit = (frame >> i) & 0x01;
+    tx_bit(bit ^ legic_prng_get_bit());
+    legic_prng_forward(1);
+  };
+
+  // disable subcarrier
+  LOW(GPIO_SSC_DOUT);
+}
+
+static void tx_ack() {
+  // wait for ack timeslot
+  last_frame_end += TAG_ACK_WAIT;
+  legic_prng_forward(TAG_ACK_WAIT/TAG_BIT_PERIOD - 1);
+  while(GetCountSspClk() < last_frame_end) { };
+
+  // transmit ack (ack is not encrypted)
+  tx_bit(true);
+  legic_prng_forward(1);
+
+  // disable subcarrier
+  LOW(GPIO_SSC_DOUT);
+}
+
+// Returns a demedulated frame or -1 on code violation
+//
+// Since TX to RX delay is arbitrary rx_frame has to:
+//  - detect start of frame (first pause)
+//  - forward prng based on ts/TAG_BIT_PERIOD
+//  - receive the frame
+//  - detect end of frame (last pause)
+static int32_t rx_frame(uint8_t *len) {
+  int32_t frame = 0;
+
+  // add 2 SSP clock cycles (1 for tx and 1 for rx pipeline delay)
+  // those will be substracted at the end of the rx phase
+  last_frame_end -= 2;
+
+  // wait for first pause (start of frame)
+  for(uint8_t i = 0; true; ++i) {
+    // increment prng every TAG_BIT_PERIOD
+    last_frame_end += TAG_BIT_PERIOD;
+    legic_prng_forward(1);
+
+    // if start of frame was received exit delay loop
+    if(wait_for(RWD_PAUSE, last_frame_end)) {
+      last_frame_end = GetCountSspClk();
+      break;
+    }
+
+    // check for code violation
+    if(i > RWD_CMD_TIMEOUT) {
+      return -1;
+    }
+  }
+
+  // receive frame
+  for(*len = 0; true; ++(*len)) {
+    // receive next bit
+    LED_D_ON();
+    int8_t bit = rx_bit();
+    LED_D_OFF();
+
+    // check for code violation and to short / long frame
+    if((bit < 0) && ((*len < RWD_MIN_FRAME_LEN) || (*len > RWD_MAX_FRAME_LEN))) {
+      return -1;
+    }
+
+    // check for code violation caused by end of frame
+    if(bit < 0) {
+      break;
+    }
+
+    // append bit
+    frame |= (bit ^ legic_prng_get_bit()) << (*len);
+    legic_prng_forward(1);
+  }
+
+  // rx_bit sets coordination timestamp to start of pause, append pause duration
+  // and substract 2 SSP clock cycles (1 for rx and 1 for tx pipeline delay) to
+  // obtain exact end of frame.
+  last_frame_end += RWD_TIME_PAUSE - 2;
+
+  return frame;
+}
+
+//-----------------------------------------------------------------------------
+// Legic Simulator
+//-----------------------------------------------------------------------------
+
+static int32_t init_card(uint8_t cardtype, legic_card_select_t *p_card) {
+  p_card->tagtype = cardtype;
+
+  switch(p_card->tagtype) {
+    case 0:
+      p_card->cmdsize = 6;
+      p_card->addrsize = 5;
+      p_card->cardsize = 22;
+      break;
+    case 1:
+      p_card->cmdsize = 9;
+      p_card->addrsize = 8;
+      p_card->cardsize = 256;
+      break;
+    case 2:
+      p_card->cmdsize = 11;
+      p_card->addrsize = 10;
+      p_card->cardsize = 1024;
+      break;
+    default:
+      p_card->cmdsize = 0;
+      p_card->addrsize = 0;
+      p_card->cardsize = 0;
+      return 2;
+  }
+  return 0;
+}
+
+static void init_tag() {
+  // configure FPGA
+  FpgaDownloadAndGo(FPGA_BITSTREAM_HF);
+  FpgaWriteConfWord(FPGA_MAJOR_MODE_HF_SIMULATOR
+                  | FPGA_HF_SIMULATOR_MODULATE_212K);
+  SetAdcMuxFor(GPIO_MUXSEL_HIPKD);
+
+  // configure SSC with defaults
+  FpgaSetupSsc();
+
+  // first pull output to low to prevent glitches then re-claim GPIO_SSC_DOUT
+  LOW(GPIO_SSC_DOUT);
+  AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT;
+  AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT;
+
+  // reserve a cardmem, meaning we can use the tracelog function in bigbuff easier.
+  legic_mem = BigBuf_get_addr();
+
+  // init crc calculator
+  crc_init(&legic_crc, 4, 0x19 >> 1, 0x05, 0);
+
+  // start 212kHz timer (running from SSP Clock)
+  StartCountSspClk();
+}
+
+// Setup reader to card connection
+//
+// The setup consists of a three way handshake:
+//  - Receive initialisation vector 7 bits
+//  - Transmit card type 6 bits
+//  - Receive Acknowledge 6 bits
+static int32_t setup_phase(legic_card_select_t *p_card) {
+  uint8_t len = 0;
+
+  // init coordination timestamp
+  last_frame_end = GetCountSspClk();
+
+  // reset prng
+  legic_prng_init(0);
+
+  // wait for iv
+  int32_t iv = rx_frame(&len);
+  if((len != 7) || (iv < 0)) {
+    return -1;
+  }
+
+  // configure prng
+  legic_prng_init(iv);
+
+  // reply with card type
+  switch(p_card->tagtype) {
+    case 0:
+      tx_frame(0x0D, 6);
+      break;
+    case 1:
+      tx_frame(0x1D, 6);
+      break;
+    case 2:
+      tx_frame(0x3D, 6);
+      break;
+  }
+
+  // wait for ack
+  int32_t ack = rx_frame(&len);
+  if((len != 6) || (ack < 0)) {
+    return -1;
+  }
+
+  // validate data
+  switch(p_card->tagtype) {
+    case 0:
+      if(ack != 0x19) return -1;
+      break;
+    case 1:
+      if(ack != 0x39) return -1;
+      break;
+    case 2:
+      if(ack != 0x39) return -1;
+      break;
+  }
+
+  // During rx the prng is clocked using the variable reader period.
+  // Since rx_frame detects end of frame by detecting a code violation,
+  // the prng is off by one bit period after each rx phase. Hence, tx
+  // code advances the prng by (TAG_FRAME_WAIT/TAG_BIT_PERIOD - 1).
+  // This is not possible for back to back rx, so this quirk reduces
+  // the gap by one period.
+  last_frame_end += TAG_BIT_PERIOD;
+
+  return 0;
+}
+
+static uint8_t calc_crc4(uint16_t cmd, uint8_t cmd_sz, uint8_t value) {
+  crc_clear(&legic_crc);
+  crc_update(&legic_crc, (value << cmd_sz) | cmd, 8 + cmd_sz);
+  return crc_finish(&legic_crc);
+}
+
+static int32_t connected_phase(legic_card_select_t *p_card) {
+  uint8_t len = 0;
+
+  // wait for command
+  int32_t cmd = rx_frame(&len);
+  if(cmd < 0) {
+    return -1;
+  }
+
+  // check if command is LEGIC_READ
+  if(len == p_card->cmdsize) {
+    // prepare data
+    uint8_t byte = legic_mem[cmd >> 1];
+    uint8_t crc = calc_crc4(cmd, p_card->cmdsize, byte);
+
+    // transmit data
+    tx_frame((crc << 8) | byte, 12);
+
+    return 0;
+  }
+
+  // check if command is LEGIC_WRITE
+  if(len == p_card->cmdsize + 8 + 4) {
+    // decode data
+    uint16_t mask = (1 << p_card->addrsize) - 1;
+    uint16_t addr = (cmd >> 1) & mask;
+    uint8_t  byte = (cmd >> p_card->cmdsize) & 0xff;
+    uint8_t  crc  = (cmd >> (p_card->cmdsize + 8)) & 0xf;
+
+    // check received against calculated crc
+    uint8_t calc_crc = calc_crc4(addr << 1, p_card->cmdsize, byte);
+    if(calc_crc != crc) {
+      Dbprintf("!!! crc mismatch: %x != %x !!!",  calc_crc, crc);
+      return -1;
+    }
+
+    // store data
+    legic_mem[addr] = byte;
+
+    // transmit ack
+    tx_ack();
+
+    return 0;
+  }
+
+  return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Command Line Interface
+//
+// Only this function is public / called from appmain.c
+//-----------------------------------------------------------------------------
+
+void LegicRfSimulate(uint8_t cardtype) {
+  // configure ARM and FPGA
+  init_tag();
+
+  // verify command line input
+  if(init_card(cardtype, &card) != 0) {
+    DbpString("Unknown tagtype.");
+    goto OUT;
+  }
+
+  LED_A_ON();
+  DbpString("Starting Legic emulator, press button to end");
+  while(!BUTTON_PRESS()) {
+    WDT_HIT();
+
+    // wait for carrier, restart after timeout
+    if(!wait_for(RWD_PULSE, GetCountSspClk() + TAG_BIT_PERIOD)) {
+      continue;
+    }
+
+    // wait for connection, restart on error
+    if(setup_phase(&card)) {
+      continue;
+    }
+
+    // conection is established, process commands until one fails
+    while(!connected_phase(&card)) {
+      WDT_HIT();
+    }
+  }
+
+OUT:
+  DbpString("Stopped");
+  FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
+  LED_A_OFF();
+  LED_C_OFF();
+  LED_D_OFF();
+  StopTicks();
+}
diff --git a/armsrc/legicrfsim.h b/armsrc/legicrfsim.h
new file mode 100644 (file)
index 0000000..c1c8a86
--- /dev/null
@@ -0,0 +1,19 @@
+//-----------------------------------------------------------------------------
+// (c) 2009 Henryk Plötz <henryk@ploetzli.ch>
+//     2018 AntiCat
+//
+// This code is licensed to you under the terms of the GNU GPL, version 2 or,
+// at your option, any later version. See the LICENSE.txt file for the text of
+// the license.
+//-----------------------------------------------------------------------------
+// LEGIC RF emulation public interface
+//-----------------------------------------------------------------------------
+
+#ifndef __LEGICRFSIM_H
+#define __LEGICRFSIM_H
+
+#include "proxmark3.h"
+
+extern void LegicRfSimulate(uint8_t tagtype);
+
+#endif /* __LEGICRFSIM_H */
index 691e1978b0b1dacfaac9797a31bb3c74c0025b72..8fbd45782f341f4e7ba064e2fe7c57102fe9ce81 100644 (file)
@@ -28,7 +28,7 @@ static command_t CommandTable[] =
   {"reader",      CmdLegicRFRead, 0, "[offset [length]] -- read bytes from a LEGIC card"},
   {"save",        CmdLegicSave,   0, "<filename> [<length>] -- Store samples"},
   {"load",        CmdLegicLoad,   0, "<filename> -- Restore samples"},
-  {"sim",         CmdLegicRfSim,  0, "[phase drift [frame drift [req/resp drift]]] Start tag simulator (use after load or read)"},
+  {"sim",         CmdLegicRfSim,  0, "[tagtype, 0:MIM22, 1:MIM256, 2:MIM1024] Start tag simulator (use after load or read)"},
   {"write",       CmdLegicRfWrite,0, "<offset> <length> -- Write sample buffer (user after load or read)"},
   {"fill",        CmdLegicRfFill, 0, "<offset> <length> <value> -- Fill/Write tag with constant value"},
   {NULL, NULL, 0, NULL}
@@ -320,10 +320,8 @@ int CmdLegicSave(const char *Cmd)
 int CmdLegicRfSim(const char *Cmd)
 {
    UsbCommand c={CMD_SIMULATE_TAG_LEGIC_RF};
-   c.arg[0] = 6;
-   c.arg[1] = 3;
-   c.arg[2] = 0;
-   sscanf(Cmd, " %" SCNi64 " %" SCNi64 " %" SCNi64, &c.arg[0], &c.arg[1], &c.arg[2]);
+   c.arg[0] = 1;
+   sscanf(Cmd, " %" SCNi64, &c.arg[0]);
    SendCommand(&c);
    return 0;
 }
index 939ba93a54ce00e9425a1156d3900f1af58c4c8e..179e87eed22bc326f37e33f6925116916701c6bf 100644 (file)
Binary files a/fpga/fpga_hf.bit and b/fpga/fpga_hf.bit differ
index 0768c29de0dcd0204d0692acc9b23486a98d26a2..78650c4a9492c2f52f5db450219e97b3bd58eacc 100644 (file)
@@ -51,38 +51,29 @@ begin
 end
 
 
-// Divide 13.56 MHz by 32 to produce the SSP_CLK
-// The register is bigger to allow higher division factors of up to /128
+// Divide 13.56 MHz to produce various frequencies for SSP_CLK
+// and modulation. 11 bits allow for factors of up to /128.
 reg [10:0] ssp_clk_divider;
 
 always @(posedge adc_clk)
     ssp_clk_divider <= (ssp_clk_divider + 1);
 
 reg ssp_clk;
-reg ssp_frame;
+
 always @(negedge adc_clk)
 begin
-    //If we're in 101, we only need a new bit every 8th carrier bit (53Hz). Otherwise, get next bit at 424Khz
     if(mod_type == 3'b101)
-    begin
-       if(ssp_clk_divider[7:0] == 8'b00000000)
-           ssp_clk <= 1'b0;
-       if(ssp_clk_divider[7:0] == 8'b10000000)
-           ssp_clk <= 1'b1;
-
-    end
+      // Get bit every at 53KHz (every 8th carrier bit of 424kHz)
+      ssp_clk <= ssp_clk_divider[7];
+    else if(mod_type == 3'b010)
+      // Get next bit at 212kHz
+      ssp_clk <= ssp_clk_divider[5];
     else
-    begin
-       if(ssp_clk_divider[4:0] == 5'd0)//[4:0] == 5'b00000)
-           ssp_clk <= 1'b1;
-       if(ssp_clk_divider[4:0] == 5'd16) //[4:0] == 5'b10000)
-           ssp_clk <= 1'b0;
-    end
+      // Get next bit at 424Khz
+      ssp_clk <= ssp_clk_divider[4];
 end
 
 
-//assign ssp_clk = ssp_clk_divider[4];
-
 // Divide SSP_CLK by 8 to produce the byte framing signal; the phase of
 // this is arbitrary, because it's just a bitstream.
 // One nasty issue, though: I can't make it work with both rx and tx at
@@ -96,19 +87,19 @@ always @(negedge ssp_clk)
     ssp_frame_divider_from_arm <= (ssp_frame_divider_from_arm + 1);
 
 
-
+reg ssp_frame;
 always @(ssp_frame_divider_to_arm or ssp_frame_divider_from_arm or mod_type)
     if(mod_type == 3'b000) // not modulating, so listening, to ARM
         ssp_frame = (ssp_frame_divider_to_arm == 3'b000);
     else
-       ssp_frame = (ssp_frame_divider_from_arm == 3'b000);
+        ssp_frame = (ssp_frame_divider_from_arm == 3'b000);
 
 // Synchronize up the after-hysteresis signal, to produce DIN.
 reg ssp_din;
 always @(posedge ssp_clk)
     ssp_din = after_hysteresis;
 
-// Modulating carrier frequency is fc/16, reuse ssp_clk divider for that
+// Modulating carrier frequency is fc/64 (212kHz) to fc/16 (848kHz). Reuse ssp_clk divider for that.
 reg modulating_carrier;
 always @(mod_type or ssp_clk or ssp_dout)
     if(mod_type == 3'b000)
@@ -116,9 +107,9 @@ always @(mod_type or ssp_clk or ssp_dout)
     else if(mod_type == 3'b001)
         modulating_carrier <= ssp_dout ^ ssp_clk_divider[3]; // XOR means BPSK
     else if(mod_type == 3'b010)
-       modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off
+        modulating_carrier <= ssp_dout & ssp_clk_divider[5]; // switch 212kHz subcarrier on/off
     else if(mod_type == 3'b100 || mod_type == 3'b101)
-       modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off
+        modulating_carrier <= ssp_dout & ssp_clk_divider[4]; // switch 424kHz modulation on/off
     else
         modulating_carrier <= 1'b0;                           // yet unused
 
@@ -133,9 +124,6 @@ assign pwr_oe4 = modulating_carrier;
 // This one is always on, so that we can watch the carrier.
 assign pwr_oe3 = 1'b0;
 
-assign dbg = modulating_carrier;
-//reg dbg;
-//always @(ssp_dout)
-//    dbg <= ssp_dout;
+assign dbg = ssp_din;
 
 endmodule
Impressum, Datenschutz