Min 4 years ago
commit
a30233f8c7
6 changed files with 782 additions and 0 deletions
  1. 3 0
      .gitignore
  2. 30 0
      platformio.ini
  3. 223 0
      programmer.py
  4. 89 0
      readme.md
  5. 82 0
      src/devices.h
  6. 355 0
      src/main.cpp

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.pio
+.vscode
+

+ 30 - 0
platformio.ini

@@ -0,0 +1,30 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+; [env:black_f407ve]
+; platform = ststm32
+; board = black_f407ve
+; framework = arduino
+; upload_protocol = stlink
+; debug_tool = stlink
+; monitor_speed = 9600
+
+[common]
+build_flags =
+    -D DEVICE=$PIOENV
+
+[env:atmega168]
+build_flags =
+    ${common.build_flags}
+platform = atmelavr
+board = nanoatmega168
+framework = arduino
+board_build.mcu = atmega168
+board_build.f_cpu = 16000000L

+ 223 - 0
programmer.py

@@ -0,0 +1,223 @@
+import serial
+from array import array
+
+class Program:
+    def __init__(self, intelhex):
+        self.program = b''
+        self.secure = b''
+        self.checksum = b''
+
+        with open(intelhex, 'r') as f:
+            mode = 0
+            for i, l in enumerate(f.readlines()):
+                l = l.rstrip()
+                if not l.startswith(':') or len(l) < 11:
+                    continue
+                size = int(l[1:3], 16)
+                addr = int(l[3:7], 16)
+                rtype = int(l[7:9], 16)
+                if len(l) != 11 + size * 2:
+                    raise ValueError(
+                        f"{intelhex}:{i} invalid number of characters, given {size * 2} found {len(l) - 11}")
+                data = bytes.fromhex(l[9:-2])
+                csum = (-sum(bytes.fromhex(l[1:-2]))) & 0x0FF
+                csum_given = int(l[-2:], 16)
+                if csum != csum_given:
+                    raise ValueError(f"{intelhex}:{i} checksum failed, given 0x{csum_given.to_bytes(1, 'big').hex()} "
+                                     f"calculated 0x{csum.to_bytes(1, 'big').hex()}")
+                if rtype == 0:
+                    if mode == 0:
+                        if addr != len(self.program):
+                            raise ValueError(f"{intelhex}:{i} expected address to be continuous")
+                        self.program += data
+                    elif mode == 16:
+                        if size != 64:
+                            raise ValueError(f"{intelhex}:{i} expected data size was 64 bytes, found {size}")
+                        self.secure = data
+                    elif mode == 32:
+                        self.checksum = data
+                elif rtype == 4:
+                    mode = int.from_bytes(data, 'big')
+                elif rtype == 1:
+                    break
+
+
+class PSoC1Prog:
+    DEVICE_IDS = {
+        9: "CY8C27143",
+        10: "CY8C27243",
+        11: "CY8C27443",
+        12: "CY8C27543",
+        13: "CY8C27643",
+        50: "CY8C24123A",
+        51: "CY8C24223A",
+        52: "CY8C24423A",
+        2225: "CY8C23533",
+        2224: "CY8C23433",
+        2226: "CY8C23033",
+        23: "CY8C21123",
+        24: "CY8C21223",
+        25: "CY8C21323",
+        54: "CY8C21234",
+        2103: "CY8C21312",
+        55: "CY8C21334",
+        56: "CY8C21434",
+        2112: "CY8C21512",
+        64: "CY8C21534",
+        73: "CY8C21634",
+        1848: "CY8CTMG110_32LTXI",
+        1849: "CY8CTMG110_00PVXI",
+        1592: "CY8CTST110_32LTXI",
+        1593: "CY8CTST110_00PVXI",
+    }
+
+    def __init__(self, serial_port):
+        self.ser = serial.Serial(port=serial_port, baudrate=9600, timeout=0.1, write_timeout=1)
+        self.ser.write(b'\n')
+        x = self.ser.read_until(b'> ')
+
+    def reinitialise(self):
+        self.ser.write(b'i')
+        res = self.ser.read_until('\r\n> ')
+
+    def reset_device(self):
+        self.ser.write(b'r')
+        res = self.ser.read_until('\r\n> ')
+
+    def get_device_id(self):
+        self.ser.write(b'd')
+        res = self.ser.read_until('\r\n> ')
+        return int(res[:-4], 16)
+
+    def get_firmware_id(self):
+        self.ser.write(b'f')
+        res = self.ser.read_until('\r\n> ')
+        return res[:-4]
+
+    def erase_memory(self):
+        self.ser.write(b'e')
+        self.ser.read_until('\r\n> ')
+
+    def read_checksum(self):
+        self.ser.write(b'c')
+        res = self.ser.read_until('\r\n> ')
+        return int(res[:-4], 16).to_bytes(2, 'big')
+
+    def read_memory(self, n=64, offset=0x80):
+        # read n x 64 memory blocks
+        blocks = b''
+        for i in range(n):
+            self.ser.write(b'r' + array('B', [i, offset]).tobytes())
+            x = self.ser.read_until('\r\n> ')
+            block = self._read_blk()
+            blocks += block
+            print(f"\rReading 0x{i.to_bytes(1, 'big').hex()} [{(i+1)/64*100:.2f}%]", end='')
+        print('')
+        return blocks
+
+    def get_device_name(self):
+        devid = self.get_device_id()
+        return self.DEVICE_IDS.get(devid, devid.to_bytes(2, 'big').hex())
+
+    def _write_blk(self, data):
+        if len(data) != 64:
+            raise ValueError("Data is not 64 bytes")
+        self.ser.write(b't')
+        self.ser.write(data)
+        x = self.ser.read_until('\r\n> ')
+        return
+
+    def _read_blk(self):
+        self.ser.write(b's')
+        blk = self.ser.read(64)
+        x = self.ser.read_until('\r\n> ')
+        return blk
+
+    def write_program(self, data):
+        self.erase_memory()
+        for i in range(len(data)//64):
+            self._write_blk(data[i*64:(i+1)*64])
+            self.ser.write(b'w' + array('B', [i]).tobytes())
+            x = self.ser.read_until('\r\n> ')
+            print(f"\rWriting 0x{i.to_bytes(1, 'big').hex()} [{(i+1)/64*100:.2f}%]", end='')
+        print('')
+
+    def write_secure(self, data):
+        self._write_blk(data)
+        self.ser.write(b'x')
+        x = self.ser.read_until('\r\n> ')
+
+    def close(self):
+        self.ser.close()
+
+
+if __name__ == '__main__':
+    import argparse
+    import sys
+
+    parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
+    parser.add_argument("port", help="Serial port")
+    parser.add_argument("cmd", choices=['flash', 'checksum', 'device', 'read', 'erase', 'reset'], 
+        help="Command to run\n" +
+        "flash - write .hex to device\n" +
+        "checksum - returns program checksum from device\n" +
+        "device - returns device name or identification hex\n" +
+        "read - dumps device program to file\n" +
+        "erase - deletes all devices program memory\n" +
+        "reset - restarts device\n"
+    )
+    parser.add_argument("-i", "--input", help="Input intel hex file for flashing")
+    parser.add_argument("-o", "--output", help="Output binary for memory dump")
+    parser.add_argument("--offset", type=int, default=0x80, help="Memory dump read address offset")
+    parser.add_argument("--count", type=int, default=64, help="Memory dump read count")
+    parser.add_argument("--read", action="store_true", help="Read back program when flashing to double check")
+    parser.add_argument("--reset", action="store_true", help="Reset device after command is complete")
+    parser.add_argument("--init", type=bool, default=True, help="Reinitialise programming mode on device")
+
+    args = parser.parse_args()
+
+    prog = PSoC1Prog(args.port)
+    if args.init:
+        prog.reinitialise()
+    if args.cmd == 'flash':
+        if args.input is None:
+            print("PSoC1_Prog: input is not specified", file=sys.stderr)
+            exit(1)
+        ifile = Program(args.input)
+        print("PSoC1_Prog: erasing memory")
+        prog.erase_memory()
+        print("PSoC1_Prog: writing program")
+        prog.write_program(ifile.program)
+        print("PSoC1_Prog: writing secure")
+        prog.write_secure(ifile.secure)
+        print("PSoC1_Prog: checking checksum")
+        csum = prog.read_checksum()
+        if csum != ifile.checksum:
+            print(f"PSoC1_Prog: checksum mismatch, device={csum.hex()} file={ifile.checksum.hex()}", file=sys.stderr)
+            exit(1)
+        if args.read:
+            mem_data = prog.read_memory()
+            if mem_data != ifile.program:
+                print(f"PSoC1_Prog: written program does not match one on device", file=sys.stderr)
+                exit(1)
+        print("PSoC1_Prog: success")
+    elif args.cmd == 'checksum':
+        print(prog.read_checksum().hex())
+    elif args.cmd == 'device':
+        print(prog.get_device_name())
+    elif args.cmd == 'read':
+        if args.output is None:
+            print("PSoC1_Prog: output is not specified", file=sys.stderr)
+            exit(1)
+        with open(args.output, 'wb') as f:
+            f.write(prog.read_memory(args.count, args.offset))
+        print("PSoC1_Prog: success")
+    elif args.cmd == 'erase':
+        prog.erase_memory()
+        print("PSoC1_Prog: success")
+    elif args.cmd == 'reset':
+        prog.reset_device()
+        exit(0)
+    if args.reset:
+        prog.reset_device()
+    exit(0)

+ 89 - 0
readme.md

@@ -0,0 +1,89 @@
+# Cypress PSoC1 Programmer
+
+This allows to program PSoC1 devices using atmega168 (Arduino Nano) board.
+
+## Setup
+
+Flash arduino nano with:
+```bash
+platformio run --target upload --environment atmega168
+```
+Connections:
+
+|Arduino | Cypress Chip |
+| --- | --- |
+| +5V | +5V |
+| GND | GND |
+| D8  | SCL |
+| D11 | XRES |
+| D12 | SDA |
+
+## Programmer
+Programmer options:
+```
+python programmer.py --help
+usage: programmer.py [-h] [-i INPUT] [-o OUTPUT] [--offset OFFSET] [--count COUNT] [--read] [--reset] [--init INIT] port {flash,checksum,device,read,erase,reset}
+
+positional arguments:
+  port                  Serial port
+  {flash,checksum,device,read,erase,reset}
+                        Command to run
+                        flash - write .hex to device
+                        checksum - returns program checksum from device
+                        device - returns device name or identification hex
+                        read - dumps device program to file
+                        erase - deletes all devices program memory
+                        reset - restarts device
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -i INPUT, --input INPUT
+                        Input intel hex file for flashing
+  -o OUTPUT, --output OUTPUT
+                        Output binary for memory dump
+  --offset OFFSET       Memory dump read address offset
+  --count COUNT         Memory dump read count
+  --read                Read back program when flashing to double check
+  --reset               Reset device after command is complete
+  --init INIT           Reinitialise programming mode on device
+```
+You can flash using .hex file made by PSoC Designer like so:
+```bash
+programmer.py {PORT} flash -i {hex file}
+```
+
+## TODO
+* Chips without XRES has special procedure to start programming mode which is not implemented.
+* Only tested on CY8C24423A chip
+
+## Supported devices
+
+ * CY8C27143
+ * CY8C27243
+ * CY8C27443
+ * CY8C27543
+ * CY8C27643
+ * CY8C24123A
+ * CY8C24223A
+ * CY8C24423A
+ * CY8C23533
+ * CY8C23433
+ * CY8C23033
+ * CY8C21123
+ * CY8C21223
+ * CY8C21323
+ * CY8C21234
+ * CY8C21312
+ * CY8C21334
+ * CY8C21434
+ * CY8C21512
+ * CY8C21534
+ * CY8C21634
+ * CY8CTMG110-32LTXI
+ * CY8CTMG110-00PVXI
+ * CY8CTST110-32LTXI
+ * CY8CTST110-00PVXI
+ * Probably other PSoC1 chips
+
+
+Project is based on [https://www.cypress.com/file/42196/download](https://www.cypress.com/file/42196/download)

File diff suppressed because it is too large
+ 82 - 0
src/devices.h


+ 355 - 0
src/main.cpp

@@ -0,0 +1,355 @@
+
+
+// #if DEVICE == atmega168
+
+#include <Arduino.h>
+#include <avr/io.h>
+
+#define SCL_PIN BIT0
+#define RES_PIN BIT3
+#define SDA_PIN BIT4
+#define LED_PIN BIT5
+
+#define DELAY for(i=0;i<1;i++);
+
+#include "devices.h"
+
+#define _CLK1 PORTB|= SCL_PIN;DELAY
+#define _CLK0 PORTB&=~SCL_PIN;DELAY
+
+
+volatile uint16_t i;
+uint8_t read_buf[2];
+uint16_t dev_id = 0;
+
+uint8_t ram_blk[64];
+
+// firmware version
+const uint16_t fw = 0x0001;
+
+
+void data_send(const uint8_t *data, uint16_t len) {
+  uint16_t k;
+  uint8_t prev = 0;
+  uint8_t curr = 1;
+
+  // PORTB |= SDA_PIN;  // Always starts with high
+  // PORTB &= ~SCL_PIN; // Make sure clock is low
+  DDRB  |= SDA_PIN;  // Making sure sdata is output
+  PORTB |= SDA_PIN | LED_PIN;
+  for (k = 0;k < len;k++) {
+    curr = (data[k/8] & BIT7 >> k%8) == 0;
+    // PORTB |= SCL_PIN; // Set clock high
+
+    if (curr != prev) {
+      prev = curr;
+      PORTB ^= SDA_PIN | SCL_PIN;
+    }
+    else
+      PORTB ^= SCL_PIN;
+    DELAY;
+    // // if binary 0
+    //   P1OUT |= BIT3; // Set clock high
+      
+    //   //uart_print("0", 1);
+    // } else {
+    // // if binary 1
+    //   P1OUT |= BIT5; // Set sdata high
+    //   P1OUT |= BIT3; // Set clock high, keeping timing consistent
+    //   prev = 1;
+    //   //uart_print("1", 1);
+    // }
+    
+    // P1OUT ^= BIT0;
+    
+    //for(i=0;i<delay;i++); //delay
+    // P1OUT ^= BIT0;
+    PORTB ^= SCL_PIN; // Set clock low
+    DELAY;
+    //for(i=0;i<delay;i++); //delay
+  }
+  //P1OUT &= ~BIT6;
+}
+
+void wait_sdata_low() {
+  PORTB &= ~(LED_PIN | SDA_PIN); // Set to high-Z
+  DDRB &= ~SDA_PIN; // Setting sdata to read
+  while((PINB & SDA_PIN) != 0); // wait until pin goes low
+
+}
+
+void data_pull() {
+  uint8_t j;
+
+  PORTB &= ~(LED_PIN | SDA_PIN); // Set to high-Z
+  DDRB &= ~SDA_PIN; // Setting sdata to read
+  while((PINB & SDA_PIN) != 0); // wait until pin goes low
+
+  // One clock
+  _CLK1;
+  _CLK0;
+
+  while((PINB & SDA_PIN) == 0); // wait until pin goes high
+  while((PINB & SDA_PIN) != 0); // wait until pin goes low
+  // This might take up to 100ms. Perhaps should check if it takes longer
+  for(j=0;j<40;j++) {
+    // Cycle clock 40 as per specification
+    _CLK1;
+    _CLK0;
+  }
+  // Setting config back to write mode
+  // DDRB &= ~SDA_PIN; 
+  // PORTB |= SDA_PIN | LED_PIN;
+}
+
+void data_read(uint8_t *buf, uint16_t size) {
+  PORTB &= ~(SDA_PIN | LED_PIN);
+  DDRB &= ~SDA_PIN; // Setting sdata to read
+
+  uint8_t k;
+  // Cycle clock
+  _CLK1;
+  _CLK0;
+  // _CLK1;
+  // _CLK0;
+
+  for(k=0;k<size;k++) {
+    // Init byte to zero
+    _CLK1;
+    _CLK0;
+    if(k%8==0) buf[k/8] = 0;
+    if((PINB & SDA_PIN) != 0) buf[k/8] |= (BIT7 >> k%8);
+    
+  }
+  _CLK1;
+  _CLK0;
+}
+
+uint8_t read_byte(uint8_t cmd) {
+  uint8_t buf[] = {0b10100000 | (cmd >> 3), 0};
+  // buf[0] &= cmd >> 3;
+  buf[1] = cmd << 5;
+  data_send(buf, 11);
+  data_read(&buf[1], 8);
+  data_send(buf, 1); // Send 1
+  return buf[1];
+}
+
+void reset_device() {
+  PORTB |= RES_PIN;
+  for(i=0;i<500;i++);
+  PORTB &= ~RES_PIN;
+}
+
+void initialise() {
+  DDRB |= SCL_PIN | SDA_PIN | LED_PIN | RES_PIN;
+  reset_device();
+  data_send(init1, init1_len);
+  data_pull();
+  data_send(init2, init2_len);
+  data_pull();
+  data_send(init3_5V, init3_len);
+}
+
+uint16_t read_id() {
+  data_send(id_setup, id_setup_len);
+  data_pull();
+  uint16_t result = 0;
+  result = read_byte(0b11111000) << 8;
+  result |= read_byte(0b11111001);
+  return result;
+}
+
+uint16_t read_checksum(uint16_t dev_id) {
+  if(dev_id == dev_CY8C24123A || dev_id == dev_CY8C24223A || dev_id == dev_CY8C24423A)
+    data_send(checksum_1, checksum_len);
+  else 
+    data_send(checksum_0, checksum_len);
+  data_pull();
+  uint16_t result = 0;
+  result = read_byte(0b11111001) << 8;
+  result |= read_byte(0b11111000);
+  return result;
+}
+
+void erase_all_memory() {
+  data_send(bulk_erase, bulk_erase_len);
+  data_pull();
+}
+
+void write_block(uint16_t dev_id, uint8_t block) {
+  uint8_t payload[3];
+  for(uint8_t ADDR = 0; ADDR < 64; ADDR++) {
+    // WRITE BYTE
+    uint8_t data = ram_blk[ADDR];
+    payload[0] = 0b10010000 | (ADDR >> 3);
+    payload[1] = (ADDR << 5) | (data >> 3);
+    payload[2] = (data << 5) | 0b00011100;
+    // 1001 0aaa  aaad dddd  ddd1 11xx
+    data_send(payload, 22);
+  }
+  payload[0] = 0b10011111;
+  payload[1] = 0b01000000 | (block >> 3);
+  payload[2] = 0b00011100 | (block << 5);
+  // 1001 1111  010d dddd  ddd1 11xx
+  data_send(payload, 22);
+
+  if(dev_id >= dev_CY8C27143 && dev_id <= dev_CY8C27643)
+    data_send(program_block_1, program_block_len);
+  else
+    data_send(program_block_0, program_block_len);
+  data_pull();
+}
+
+void write_secure() {
+  uint8_t payload[3];
+  for(uint8_t ADDR = 0; ADDR < 64; ADDR++) {
+    // WRITE BYTE
+    uint8_t data = ram_blk[ADDR];
+    payload[0] = 0b10010000 | (ADDR >> 3);
+    payload[1] = (ADDR << 5) | (data >> 3);
+    payload[2] = (data << 5) | 0b00011100;
+    // 1001 0aaa  aaad dddd  ddd1 11xx
+    data_send(payload, 22);
+  }
+  
+  data_send(secure, secure_len);
+  data_pull();
+}
+
+void read_block(uint8_t block, uint8_t offset) {
+  uint8_t payload[3];
+
+  // SET-BLOCK-NUM
+  payload[0] = 0b10011111;
+  payload[1] = 0b01000000 | (block >> 3);
+  payload[2] = 0b00011100 | (block << 5);
+  // 1001 1111  010d dddd  ddd1 11xx
+  data_send(payload, 22);
+  data_send(verify_setup, verify_setup_len);
+  data_pull();
+    
+  for(uint8_t ADDR = 0; ADDR < 64; ADDR++) {
+    ram_blk[ADDR] = read_byte(ADDR + offset);
+  }
+}
+
+void write_program(uint16_t dev_id, uint16_t blocks) {
+  uint8_t payload[3];
+  erase_all_memory();
+  for(uint16_t BLK_NUM = 0; BLK_NUM < blocks; BLK_NUM++) {
+    Serial.readBytes(ram_blk, 64);
+    for(uint8_t ADDR = 0; ADDR < 64; ADDR++) {
+      // WRITE BYTE
+      uint8_t data = ram_blk[ADDR];
+      payload[0] = 0b10010000 | (ADDR >> 3);
+      payload[1] = (ADDR << 5) | (data >> 3);
+      payload[2] = (data << 5) | 0b00011100;
+      // 1001 0aaa  aaad dddd  ddd1 11xx
+      data_send(payload, 22);
+    }
+    Serial.write(BLK_NUM);
+    // SET-BLOCK-NUM
+    payload[0] = 0b10011111;
+    payload[1] = 0b01000000 | (BLK_NUM >> 3);
+    payload[2] = 0b00011100 | (BLK_NUM << 5);
+    // 1001 1111  010d dddd  ddd1 11xx
+    data_send(payload, 22);
+
+    if(dev_id >= dev_CY8C27143 && dev_id <= dev_CY8C27643)
+      data_send(program_block_1, program_block_len);
+    else
+      data_send(program_block_0, program_block_len);
+    data_pull();
+  }
+
+}
+
+void read_program(uint16_t blocks, uint8_t offset) {
+  uint8_t payload[3];
+  uint8_t data;
+
+  for(uint16_t BLK_NUM = 0; BLK_NUM < blocks; BLK_NUM++) {
+    // SET-BLOCK-NUM
+    payload[0] = 0b10011111;
+    payload[1] = 0b01000000 | (BLK_NUM >> 3);
+    payload[2] = 0b00011100 | (BLK_NUM << 5);
+    // 1001 1111  010d dddd  ddd1 11xx
+    data_send(payload, 22);
+    data_send(verify_setup, verify_setup_len);
+    data_pull();
+    
+    for(uint8_t ADDR = 0; ADDR < 64; ADDR++) {
+      ram_blk[ADDR] = read_byte(ADDR + offset);
+    }
+    Serial.write(ram_blk, 64);
+    Serial.write(BLK_NUM);
+  }
+}
+
+void setup(void) {
+  Serial.begin(9600);
+  initialise();
+  dev_id = read_id();
+  Serial.print("> ");
+}
+
+void loop() {
+  uint8_t command;
+  if(Serial.readBytes(&command, 1) > 0) {
+    if(command == 'd') {
+      Serial.println(dev_id, HEX);
+    }
+    else if(command == 'i') {
+      initialise();
+      dev_id = read_id();
+      Serial.println();
+    }
+    else if(command == 'r') {
+      uint8_t args[2] = {0x00, 0x80};
+      Serial.readBytes(args, 2);
+      read_block(args[0], args[1]);
+      Serial.println();
+    }
+    else if(command == 'e') {
+      erase_all_memory();
+      Serial.println();
+    }
+    else if(command == 'w') {
+      // Write block
+      if(Serial.readBytes(&command, 1) > 0) {
+        write_block(dev_id, command);
+      }
+      Serial.println();
+    }
+    else if(command == 'f') {
+      Serial.println(fw, HEX);
+    }
+    else if(command == 'c') {
+      Serial.println(read_checksum(dev_id), HEX);
+    }
+    else if(command == 's') {
+      Serial.write(ram_blk, 64);
+      Serial.println();
+    }
+    else if(command == 't') {
+      Serial.readBytes(ram_blk, 64);
+      Serial.println();
+    }
+    else if(command == 'x') {
+      write_secure();
+      Serial.println();
+    }
+    else if(command == 'a') {
+      reset_device();
+      Serial.println();
+    }
+    else if(command == '\n') {
+      Serial.println();
+    }
+    else {
+      Serial.println("invalid");
+    }
+    Serial.print("> ");
+  }
+}