fix read for verify_image
[openocd.git] / src / target / dsp5680xx.c
index f2f2dba63d53c89721afc30e044c27b45876e147..da9ba46a7556ebad426bfc93c2e321d47cf16166 100644 (file)
@@ -28,7 +28,7 @@
 #include "target_type.h"
 #include "dsp5680xx.h"
 
-#define err_check(retval,err_msg) if(retval != ERROR_OK){LOG_ERROR("%s: %s.",__FUNCTION__,err_msg);return retval;}
+#define err_check(retval,err_msg) if(retval != ERROR_OK){LOG_ERROR("%s: %d %s.",__FUNCTION__,__LINE__,err_msg);return retval;}
 #define err_check_propagate(retval) if(retval!=ERROR_OK){return retval;}
 
 // Forward declarations, could try to optimize this.
@@ -158,16 +158,11 @@ int dsp5680xx_target_status(struct target * target, uint8_t * jtag_st, uint16_t
 }
 
 static int dsp5680xx_assert_reset(struct target *target){
-  //TODO verify the sleeps are necessary
-  jtag_add_reset(1,0);
   target->state = TARGET_RESET;
-  jtag_add_sleep(500);
-  usleep(1000);
   return ERROR_OK;
 }
 
 static int dsp5680xx_deassert_reset(struct target *target){
-  jtag_add_reset(0,0);
   target->state = TARGET_RUNNING;
   return ERROR_OK;
 }
@@ -179,7 +174,6 @@ static int dsp5680xx_poll(struct target *target){
   uint16_t read_tmp;
   retval = dsp5680xx_jtag_status(target,&jtag_status);
   err_check_propagate(retval);
-  LOG_DEBUG("JTAG 0x%02X",jtag_status);//TODO remove!!
   if (jtag_status == JTAG_STATUS_DEBUG)
     if (target->state != TARGET_HALTED){
       retval = eonce_enter_debug_mode(target,&read_tmp);
@@ -250,17 +244,6 @@ static int eonce_read_status_reg(struct target * target, uint16_t * data){
   return retval;
 }
 
-static int dsp5680xx_obase_addr(struct target * target, uint32_t * addr){
-  // Finds out the default value of the OBASE register address.
-  int retval;
-  uint32_t data_to_shift_into_dr;// just to make jtag happy
-  retval = eonce_instruction_exec(target,DSP5680XX_ONCE_OBASE,1,0,0,NULL);
-  err_check_propagate(retval);
-  retval = dsp5680xx_drscan(target,(uint8_t *)& data_to_shift_into_dr,(uint8_t *) addr, 8);
-  err_check_propagate(retval);
-  return retval;
-}
-
 static int dsp5680xx_halt(struct target *target){
   int retval;
   uint8_t jtag_status;
@@ -620,25 +603,15 @@ static int eonce_move_value_to_pc(struct target * target, uint32_t value)
 
 static int eonce_load_TX_RX_to_r0(struct target * target)
 {
-  //TODO add error control
-  uint32_t obase_addr;
-  int retval = dsp5680xx_obase_addr(target,& obase_addr);
-  eonce_move_long_to_r0(target,((MC568013_EONCE_TX_RX_ADDR)+(obase_addr<<16)));
+  int retval;
+  retval = eonce_move_long_to_r0(target,((MC568013_EONCE_TX_RX_ADDR)+(MC568013_EONCE_OBASE_ADDR<<16)));
   return retval;
 }
 
 static int eonce_load_TX_RX_high_to_r0(struct target * target)
 {
-  //TODO add error control
-  uint32_t obase_addr;
-  int retval = dsp5680xx_obase_addr(target,& obase_addr);
-  err_check_propagate(retval);
-  if(!(obase_addr && 0xff)){
-       LOG_USER("%s: OBASE address read as 0x%04X instead of 0xFF.",__FUNCTION__,obase_addr);
-       return ERROR_FAIL;
-  }
-  eonce_move_long_to_r0(target,((MC568013_EONCE_TX1_RX1_HIGH_ADDR)+(obase_addr<<16)));
-  err_check_propagate(retval);
+  int retval = 0;
+  retval = eonce_move_long_to_r0(target,((MC568013_EONCE_TX1_RX1_HIGH_ADDR)+(MC568013_EONCE_OBASE_ADDR<<16)));
   return retval;
 }
 
@@ -723,7 +696,7 @@ static int dsp5680xx_read_32_single(struct target * target, uint32_t address, ui
   uint16_t tmp;
   retval = eonce_rx_upper_data(target,&tmp);
   err_check_propagate(retval);
-  *data_read = (((*data_read)<<16) | tmp);
+  *data_read = ((tmp<<16) | (*data_read));//This enables opencd crc to succeed, even though it's very slow.
   return retval;
 }
 
@@ -741,7 +714,14 @@ static int dsp5680xx_read(struct target * target, uint32_t address, unsigned siz
   retval = dsp5680xx_convert_address(&address, &pmem);
   err_check_propagate(retval);
 
+  context.flush = 0;
+  int counter = FLUSH_COUNT_READ_WRITE;
+
   for (unsigned i=0; i<count; i++){
+    if(--counter==0){
+      context.flush = 1;
+      counter = FLUSH_COUNT_FLASH;
+    }
     switch (size){
     case 1:
       if(!(i%2)){
@@ -761,7 +741,13 @@ static int dsp5680xx_read(struct target * target, uint32_t address, unsigned siz
       break;
     }
        err_check_propagate(retval);
+    context.flush = 0;
   }
+  
+  context.flush = 1;
+  retval = dsp5680xx_execute_queue();
+  err_check_propagate(retval);
+  
   return retval;
 }
 
@@ -811,22 +797,19 @@ static int dsp5680xx_write_8(struct target * target, uint32_t address, uint32_t
   uint16_t * data_w = (uint16_t *)data;
   uint32_t iter;
 
-  int counter_reset = FLUSH_COUNT_WRITE;
-  int counter = counter_reset;
-
+  int counter = FLUSH_COUNT_READ_WRITE;
   for(iter = 0; iter<count/2; iter++){
-       if(--counter==0){
-         context.flush = 1;
-         counter = counter_reset;
-       }
-
+    if(--counter==0){
+      context.flush = 1;
+      counter = FLUSH_COUNT_READ_WRITE;
+    }
     retval = dsp5680xx_write_16_single(target,address+iter,data_w[iter], pmem);
     if(retval != ERROR_OK){
       LOG_ERROR("%s: Could not write to p:0x%04X",__FUNCTION__,address);
-         context.flush = 1;
+      context.flush = 1;
       return retval;
     }
-       context.flush = 0;
+    context.flush = 0;
   }
   context.flush = 1;
 
@@ -853,14 +836,12 @@ static int dsp5680xx_write_16(struct target * target, uint32_t address, uint32_t
        err_check(retval,"Target must be halted.");
   };
   uint32_t iter;
-
-  int counter_reset = FLUSH_COUNT_WRITE;
-  int counter = counter_reset;
+  int counter = FLUSH_COUNT_READ_WRITE;
 
   for(iter = 0; iter<count; iter++){
        if(--counter==0){
          context.flush = 1;
-         counter = counter_reset;
+      counter = FLUSH_COUNT_READ_WRITE;
        }
     retval = dsp5680xx_write_16_single(target,address+iter,data[iter], pmem);
     if(retval != ERROR_OK){
@@ -881,14 +862,12 @@ static int dsp5680xx_write_32(struct target * target, uint32_t address, uint32_t
        err_check(retval,"Target must be halted.");
   };
   uint32_t iter;
-
-  int counter_reset = FLUSH_COUNT_WRITE;
-  int counter = counter_reset;
+  int counter = FLUSH_COUNT_READ_WRITE;
 
   for(iter = 0; iter<count; iter++){
        if(--counter==0){
          context.flush = 1;
-         counter = counter_reset;
+      counter = FLUSH_COUNT_READ_WRITE;
        }
     retval = dsp5680xx_write_32_single(target,address+(iter<<1),data[iter], pmem);
     if(retval != ERROR_OK){
@@ -904,7 +883,7 @@ static int dsp5680xx_write_32(struct target * target, uint32_t address, uint32_t
 
 //TODO doxy
 static int dsp5680xx_write(struct target *target, uint32_t address, uint32_t size, uint32_t count, const uint8_t * buffer){
-  //TODO Cannot write 32bit to odd address, will write 0x1234567 to as 0x5678 0x0012
+  //TODO Cannot write 32bit to odd address, will write 0x1234567 as 0x5678 0x0012
   if(target->state != TARGET_HALTED){
     LOG_USER("Target must be halted.");
     return ERROR_OK;
@@ -963,27 +942,32 @@ static int dsp5680xx_read_buffer(struct target * target, uint32_t address, uint3
     LOG_USER("Target must be halted.");
     return ERROR_OK;
   }
-  // byte addressing!
-  int retval = ERROR_OK;
-  int pmem = 1;
-  uint16_t tmp_wrd= 0;
-
-  retval = dsp5680xx_convert_address(&address, &pmem);
-  err_check_propagate(retval);
-
-  for (unsigned i=0; i<size; i++)
-    if(!(i%2)){
-      retval = dsp5680xx_read_16_single(target, address + i/2, &tmp_wrd, pmem);
-         err_check_propagate(retval);
-      //TODO find a better solution. endiannes differs from normal read, otherwise the openocd crc would do weird stuff.
-      buffer[i+1] = (uint8_t) (tmp_wrd>>8);
-      buffer[i] = (uint8_t) (tmp_wrd&0xff);
-   }
-  return retval;
+  // read_buffer is called when the verify_image command is executed.
+  // The "/2" solves the byte/word addressing issue. 
+  return dsp5680xx_read(target,address,2,size/2,buffer);
 }
 
 static int dsp5680xx_checksum_memory(struct target * target, uint32_t address, uint32_t size, uint32_t * checksum){
-  return ERROR_FAIL; //this makes openocd do the crc
+  return ERROR_FAIL;// This will make OpenOCD do the read out the data and verify it.
+}
+
+// Data signature algorithm used by the core FM (flash module)
+static int perl_crc(uint16_t * buff16,uint32_t  word_count){
+  uint16_t checksum = 0xffff;
+  uint16_t data,fbmisr;
+  uint32_t i;
+  for(i=0;i<word_count;i++){
+    data = buff16[i];
+    fbmisr = (checksum & 2)>>1 ^ (checksum & 4)>>2 ^ (checksum & 16)>>4 ^ (checksum & 0x8000)>>15;
+    checksum = (data ^ ((checksum << 1) | fbmisr));
+  }
+  i--;
+  for(;!(i&0x80000000);i--){
+    data = buff16[i];
+    fbmisr = (checksum & 2)>>1 ^ (checksum & 4)>>2 ^ (checksum & 16)>>4 ^ (checksum & 0x8000)>>15;
+    checksum = (data ^ ((checksum << 1) | fbmisr));
+  }
+  return checksum;
 }
 
 int dsp5680xx_f_SIM_reset(struct target * target){
@@ -1009,33 +993,23 @@ static int dsp5680xx_soft_reset_halt(struct target *target){
   return retval;
 }
 
-int dsp5680xx_f_protect_check(struct target * target, uint8_t * protected) {
-  uint16_t i,j;
+int dsp5680xx_f_protect_check(struct target * target, uint16_t * protected) {
+  uint16_t aux;
   int retval;
   if (dsp5680xx_target_status(target,NULL,NULL) != TARGET_HALTED){
     retval = dsp5680xx_halt(target);
        err_check_propagate(retval);
   }
-  retval = eonce_load_TX_RX_high_to_r0(target);
-  err_check_propagate(retval);
-  retval = eonce_move_value_to_y0(target,0x1234);
-  err_check_propagate(retval);
-  retval = eonce_move_y0_at_r0(target);
-  err_check_propagate(retval);
-  retval = eonce_rx_upper_data(target,&i);
-  err_check_propagate(retval);
-  retval = eonce_move_value_to_y0(target,0x4321);
-  err_check_propagate(retval);
-  retval = eonce_move_y0_at_r0(target);
-  err_check_propagate(retval);
-  retval = eonce_rx_upper_data(target,&j);
+  if(protected == NULL){
+    err_check(ERROR_FAIL,"NULL pointer not valid.");
+  }
+  retval = dsp5680xx_read_16_single(target,HFM_BASE_ADDR|HFM_PROT,&aux,0);
   err_check_propagate(retval);
-  if(protected!=NULL)
-    *protected = (uint8_t) ((i!=0x1234)||(j!=0x4321));
+  *protected = aux;
   return retval;
 }
 
-static int dsp5680xx_f_execute_command(struct target * target, uint16_t command, uint32_t address, uint16_t * hfm_ustat, int pmem){
+static int dsp5680xx_f_execute_command(struct target * target, uint16_t command, uint32_t address, uint32_t data, uint16_t * hfm_ustat, int pmem){
   int retval;
   retval = eonce_load_TX_RX_high_to_r0(target);
   err_check_propagate(retval);
@@ -1067,6 +1041,8 @@ static int dsp5680xx_f_execute_command(struct target * target, uint16_t command,
   err_check_propagate(retval);
   retval = eonce_move_value_at_r2_disp(target,0x00,HFM_PROTB);         // write to HMF_PROTB, clear protection
   err_check_propagate(retval);
+  retval = eonce_move_value_to_y0(target,data);
+  err_check_propagate(retval);
   retval = eonce_move_long_to_r3(target,address);                      // write to the flash block
   err_check_propagate(retval);
   if (pmem){
@@ -1135,32 +1111,32 @@ static int eonce_set_hfmdiv(struct target * target){
   return ERROR_OK;
 }
 
-int dsp5680xx_f_erase_check(struct target * target, uint8_t * erased){
+static int dsp5680xx_f_signature(struct target * target, uint32_t address, uint32_t words, uint16_t * signature){
   int retval;
   uint16_t hfm_ustat;
   if (dsp5680xx_target_status(target,NULL,NULL) != TARGET_HALTED){
-    retval = dsp5680xx_halt(target);
+    retval = eonce_enter_debug_mode(target,NULL);
     err_check_propagate(retval);
   }
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  // Check security
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  uint8_t protected;
-  retval = dsp5680xx_f_protect_check(target,&protected);
+  retval = dsp5680xx_f_execute_command(target,HFM_CALCULATE_DATA_SIGNATURE,address,words,&hfm_ustat,1);
   err_check_propagate(retval);
-  if(protected){
-       retval = ERROR_TARGET_FAILURE;
-       err_check(retval,"Failed to erase, flash is still protected.");
+  if (hfm_ustat&HFM_USTAT_MASK_PVIOL_ACCER){
+    retval = ERROR_TARGET_FAILURE;
+    err_check(retval,"HFM exec error:pviol and/or accer bits set.");
   }
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  // Set hfmdiv
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  retval = eonce_set_hfmdiv(target);
-  err_check_propagate(retval);
+  retval = dsp5680xx_read_16_single(target, HFM_BASE_ADDR|HFM_DATA, signature, 0);
+  return retval;
+}
 
+int dsp5680xx_f_erase_check(struct target * target, uint8_t * erased,uint32_t sector){
+  int retval;
+  uint16_t hfm_ustat;
+  if (dsp5680xx_target_status(target,NULL,NULL) != TARGET_HALTED){
+    retval = dsp5680xx_halt(target);
+    err_check_propagate(retval);
+  }
   // Check if chip is already erased.
-  // Since only mass erase is currently implemented, only the first sector is checked (assuming no code will leave it unused)
-  retval = dsp5680xx_f_execute_command(target,HFM_ERASE_VERIFY,HFM_FLASH_BASE_ADDR+0*HFM_SECTOR_SIZE,&hfm_ustat,1); // blank check
+  retval = dsp5680xx_f_execute_command(target,HFM_ERASE_VERIFY,HFM_FLASH_BASE_ADDR+sector*HFM_SECTOR_SIZE/2,0,&hfm_ustat,1); // blank check
   err_check_propagate(retval);
   if (hfm_ustat&HFM_USTAT_MASK_PVIOL_ACCER){
        retval = ERROR_TARGET_FAILURE;
@@ -1170,17 +1146,25 @@ int dsp5680xx_f_erase_check(struct target * target, uint8_t * erased){
     *erased = (uint8_t)(hfm_ustat&HFM_USTAT_MASK_BLANK);
   return retval;
 }
+  
+static int erase_sector(struct target * target, int sector, uint16_t * hfm_ustat){
+  int retval;
+  retval = dsp5680xx_f_execute_command(target,HFM_PAGE_ERASE,HFM_FLASH_BASE_ADDR+sector*HFM_SECTOR_SIZE/2,0,hfm_ustat,1);
+  err_check_propagate(retval);
+  return retval;
+}
+static int mass_erase(struct target * target, uint16_t * hfm_ustat){
+  int retval;
+  retval = dsp5680xx_f_execute_command(target,HFM_MASS_ERASE,0,0,hfm_ustat,1);
+  return retval;
+}
 
 int dsp5680xx_f_erase(struct target * target, int first, int last){
-  //TODO implement erasing individual sectors.
   int retval;
-  if(first||last){
-       retval = ERROR_FAIL;
-       err_check(retval,"Sector erasing not implemented. Call with first=last=0.");
-  }
   if (dsp5680xx_target_status(target,NULL,NULL) != TARGET_HALTED){
     retval = dsp5680xx_halt(target);
-       err_check_propagate(retval);
+    err_check_propagate(retval);
   }
   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   // Reset SIM
@@ -1188,49 +1172,25 @@ int dsp5680xx_f_erase(struct target * target, int first, int last){
   retval = dsp5680xx_f_SIM_reset(target);
   err_check_propagate(retval);
   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  // Check security
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  uint8_t protected;
-  retval = dsp5680xx_f_protect_check(target,&protected);
-  err_check_propagate(retval);
-  if(protected){
-       retval = ERROR_TARGET_FAILURE;
-       err_check(retval,"Cannot flash, security is still enabled.");
-  }
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   // Set hfmdiv
   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   retval = eonce_set_hfmdiv(target);
   err_check_propagate(retval);
 
-  // Check if chip is already erased.
-  // Since only mass erase is currently implemented, only the first sector is checked (assuming no code will leave it unused)
-  uint8_t erased;
-  retval = dsp5680xx_f_erase_check(target,&erased);
-  err_check_propagate(retval);
-  if (erased)
-    LOG_USER("Flash blank - mass erase skipped.");
-  else{
-    // Execute mass erase command.
-       uint16_t hfm_ustat;
-       uint16_t hfm_cmd = HFM_MASS_ERASE;
-    retval = dsp5680xx_f_execute_command(target,hfm_cmd,HFM_FLASH_BASE_ADDR+0*HFM_SECTOR_SIZE,&hfm_ustat,1);
-       err_check_propagate(retval);
-    if (hfm_ustat&HFM_USTAT_MASK_PVIOL_ACCER){
-         retval = ERROR_TARGET_FAILURE;
-         err_check(retval,"pviol and/or accer bits set. HFM command execution error");
-    }
-    // Verify flash was successfully erased.
-    retval = dsp5680xx_f_erase_check(target,&erased);
+  uint16_t hfm_ustat;
+  int do_mass_erase = ((!(first|last)) || ((first==0)&&(last == (HFM_SECTOR_COUNT-1))));
+  if(do_mass_erase){
+    //Mass erase
+    retval = mass_erase(target,&hfm_ustat);
+    err_check_propagate(retval);
+    last = HFM_SECTOR_COUNT-1;
+  }else{
+    for(int i = first;i<=last;i++){
+      retval = erase_sector(target,i,&hfm_ustat);
        err_check_propagate(retval);
-       if(retval == ERROR_OK){
-      if (erased)
-               LOG_USER("Flash mass erased and checked blank.");
-      else
-               LOG_WARNING("Flash mass erased, but still not blank!");
-    }
   }
-  return retval;
+  }
+  return ERROR_OK;
 }
 
 // Algorithm for programming normal p: flash
@@ -1281,16 +1241,6 @@ int dsp5680xx_f_wr(struct target * target, uint8_t *buffer, uint32_t address, ui
        err_check_propagate(retval);
   }
   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  // Check if flash is erased
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
-  uint8_t erased;
-  retval = dsp5680xx_f_erase_check(target,&erased);
-  err_check_propagate(retval);
-  if(!erased){
-       retval = ERROR_FAIL;
-       err_check(retval,"Flash must be erased before flashing.");
-  }
-  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   // Download the pgm that flashes.
   // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
   uint32_t my_favourite_ram_address = 0x8700; // This seems to be a safe address. This one is the one used by codewarrior in 56801x_flash.cfg
@@ -1339,14 +1289,14 @@ int dsp5680xx_f_wr(struct target * target, uint8_t *buffer, uint32_t address, ui
   retval = dsp5680xx_resume(target,0,my_favourite_ram_address,0,0);
   err_check_propagate(retval);
 
-  int counter_reset = FLUSH_COUNT_FLASH;
-  int counter = counter_reset;
+  int counter = FLUSH_COUNT_FLASH;
   context.flush = 0;
-  for(uint32_t i=1; (i<count/2)&&(i<HFM_SIZE_REAL); i++){
-       if(--counter==0){
-         context.flush = 1;
-         counter = counter_reset;
-       }
+  uint32_t i;
+  for(i=1; (i<count/2)&&(i<HFM_SIZE_WORDS); i++){
+    if(--counter==0){
+      context.flush = 1;
+      counter = FLUSH_COUNT_FLASH;
+    }
     retval = eonce_tx_upper_data(target,buff16[i],&drscan_data);
        if(retval!=ERROR_OK){
          context.flush = 1;
@@ -1355,9 +1305,23 @@ int dsp5680xx_f_wr(struct target * target, uint8_t *buffer, uint32_t address, ui
        context.flush = 0;
   }
   context.flush = 1;
+  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+  // Verify flash
+  // -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
+  uint16_t signature;
+  uint16_t pc_crc;
+  retval =  dsp5680xx_f_signature(target,address,i,&signature);
+  err_check_propagate(retval);
+  pc_crc = perl_crc(buff16,i);
+  if(pc_crc != signature){
+    retval = ERROR_FAIL;
+    err_check(retval,"Flashed data failed CRC check, flash again!");
+  }
   return retval;
 }
 
+
+
 int dsp5680xx_f_unlock(struct target * target){
   int retval;
   if(target->tap->enabled){

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)