Skip to content

Instantly share code, notes, and snippets.

@jboone
Created June 20, 2014 05:59
Show Gist options
  • Save jboone/bbdb3aa3c038d302d920 to your computer and use it in GitHub Desktop.
Save jboone/bbdb3aa3c038d302d920 to your computer and use it in GitHub Desktop.
Code to program PortaPack CPLD (Altera MAX V) based on process in .SVF file.
static void portapack_cpld_jtag_tck(const uint_fast8_t value) {
if(value) {
gpio_set(PORTAPACK_CPLD_JTAG_TCK_GPIO_PORT, PORTAPACK_CPLD_JTAG_TCK_GPIO_BIT);
} else {
gpio_clear(PORTAPACK_CPLD_JTAG_TCK_GPIO_PORT, PORTAPACK_CPLD_JTAG_TCK_GPIO_BIT);
}
}
static void portapack_cpld_jtag_tdi(const uint_fast8_t value) {
if(value) {
gpio_set(PORTAPACK_CPLD_JTAG_TDI_GPIO_PORT, PORTAPACK_CPLD_JTAG_TDI_GPIO_BIT);
} else {
gpio_clear(PORTAPACK_CPLD_JTAG_TDI_GPIO_PORT, PORTAPACK_CPLD_JTAG_TDI_GPIO_BIT);
}
}
static void portapack_cpld_jtag_tms(const uint_fast8_t value) {
if(value) {
gpio_set(PORTAPACK_CPLD_JTAG_TMS_GPIO_PORT, PORTAPACK_CPLD_JTAG_TMS_GPIO_BIT);
} else {
gpio_clear(PORTAPACK_CPLD_JTAG_TMS_GPIO_PORT, PORTAPACK_CPLD_JTAG_TMS_GPIO_BIT);
}
}
static uint_fast8_t portapack_cpld_jtag_tdo() {
//return (GPIO_PIN(PORTAPACK_CPLD_JTAG_TDO_GPIO_PORT) >> PORTAPACK_CPLD_JTAG_TMS_GPIO_PIN) & 1;
return gpio_get(PORTAPACK_CPLD_JTAG_TDO_GPIO_PORT, PORTAPACK_CPLD_JTAG_TDO_GPIO_BIT);
}
static uint_fast8_t portapack_cpld_jtag_clock() {
portapack_cpld_jtag_tck(1);
delay(2);
portapack_cpld_jtag_tck(0);
delay(2);
return portapack_cpld_jtag_tdo();
}
static uint_fast8_t portapack_cpld_jtag_tms_clock(const uint_fast8_t value) {
portapack_cpld_jtag_tms(value);
return portapack_cpld_jtag_clock();
}
static void portapack_cpld_jtag_runtest_tck(const size_t count) {
for(size_t i=0; i<count; i++) {
//portapack_cpld_jtag_clock();
delay(4);
}
}
void portapack_cpld_jtag_reset() {
portapack_cpld_jtag_tms(1);
for(size_t i=0; i<8; i++) {
portapack_cpld_jtag_clock();
}
/* Test-Logic-Reset */
}
static uint32_t portapack_cpld_jtag_shift(const uint_fast8_t count, uint32_t value) {
/* Scan */
portapack_cpld_jtag_tms_clock(0);
/* Capture */
portapack_cpld_jtag_tms_clock(0);
/* Shift */
for(size_t i=0; i<count; i++) {
const uint_fast8_t tdo = portapack_cpld_jtag_tdo();
portapack_cpld_jtag_tdi(value & 1);
const uint_fast8_t tms = (i == (count - 1)) ? 1 : 0;
portapack_cpld_jtag_tms_clock(tms);
value >>= 1;
value |= tdo << (count - 1);
/* Shift or Exit1 */
}
portapack_cpld_jtag_tms_clock(1);
/* Update */
portapack_cpld_jtag_tms_clock(0);
/* Run-Test/Idle */
return value;
}
static uint32_t portapack_cpld_jtag_shift_ir(const uint_fast8_t count, const uint32_t value) {
/* Run-Test/Idle */
portapack_cpld_jtag_tms_clock(1);
/* Select-DR-Scan */
portapack_cpld_jtag_tms_clock(1);
/* Select-IR-Scan */
return portapack_cpld_jtag_shift(count, value);
}
static uint32_t portapack_cpld_jtag_shift_dr(const uint_fast8_t count, const uint32_t value) {
/* Run-Test/Idle */
portapack_cpld_jtag_tms_clock(1);
/* Select-DR-Scan */
return portapack_cpld_jtag_shift(count, value);
}
static const uint16_t portapack_cpld_jtag_block_0[] = { ...data transformed from .SVF... };
static const uint16_t portapack_cpld_jtag_block_1[] = { ...data transformed from .SVF... };
static void portapack_cpld_jtag_program_block(const uint16_t* const data, const size_t count) {
for(size_t i=0; i<count; i++) {
portapack_cpld_jtag_shift_dr(16, data[i]);
portapack_cpld_jtag_runtest_tck(1800);
}
}
static bool portapack_cpld_jtag_verify_block(const uint16_t* const data, const size_t count) {
bool failure = false;
for(size_t i=0; i<count; i++) {
const uint16_t from_device = portapack_cpld_jtag_shift_dr(16, 0xffff);
if( from_device != data[i] ) {
failure = true;
}
}
return (failure == false);
}
void portapack_cpld_jtag_io_init() {
/* Configure pins to GPIO */
scu_pinmux(PORTAPACK_CPLD_JTAG_TDI_SCU_PIN, SCU_GPIO_PUP | PORTAPACK_CPLD_JTAG_TDI_SCU_FUNCTION);
scu_pinmux(PORTAPACK_CPLD_JTAG_TMS_SCU_PIN, SCU_GPIO_PUP | PORTAPACK_CPLD_JTAG_TMS_SCU_FUNCTION);
scu_pinmux(PORTAPACK_CPLD_JTAG_TCK_SCU_PIN, SCU_GPIO_PDN | PORTAPACK_CPLD_JTAG_TCK_SCU_FUNCTION);
scu_pinmux(PORTAPACK_CPLD_JTAG_TDO_SCU_PIN, SCU_GPIO_PDN | PORTAPACK_CPLD_JTAG_TDO_SCU_FUNCTION);
portapack_cpld_jtag_tdi(1);
portapack_cpld_jtag_tms(1);
portapack_cpld_jtag_tck(0);
/* Configure pin directions */
GPIO_DIR(PORTAPACK_CPLD_JTAG_TDI_GPIO_PORT) |= PORTAPACK_CPLD_JTAG_TDI_GPIO_BIT;
GPIO_DIR(PORTAPACK_CPLD_JTAG_TMS_GPIO_PORT) |= PORTAPACK_CPLD_JTAG_TMS_GPIO_BIT;
GPIO_DIR(PORTAPACK_CPLD_JTAG_TCK_GPIO_PORT) |= PORTAPACK_CPLD_JTAG_TCK_GPIO_BIT;
GPIO_DIR(PORTAPACK_CPLD_JTAG_TDO_GPIO_PORT) &= ~PORTAPACK_CPLD_JTAG_TDO_GPIO_BIT;
}
#include "linux_stuff.h"
bool portapack_cpld_jtag_program() {
/* Unknown state */
portapack_cpld_jtag_reset();
/* Test-Logic-Reset */
portapack_cpld_jtag_tms_clock(0);
/* Run-Test/Idle */
portapack_cpld_jtag_shift_ir(10, 0b0000000110);
const uint32_t idcode = portapack_cpld_jtag_shift_dr(32, 0);
if( idcode != 0x020A50DD ) {
return false;
}
/* Enter ISP:
* Ensures that the I/O pins transition smoothly from user mode to ISP
* mode.
*/
portapack_cpld_jtag_shift_ir(10, 0x2cc);
portapack_cpld_jtag_runtest_tck(18003); // 1ms
/* Check ID:
* The silicon ID is checked before any Program or Verify process. The
* time required to read this silicon ID is relatively small compared to
* the overall programming time.
*/
portapack_cpld_jtag_shift_ir(10, 0x203);
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0089);
portapack_cpld_jtag_shift_ir(10, 0x205);
portapack_cpld_jtag_runtest_tck(93); // 5us
uint16_t silicon_id[5];
silicon_id[0] = portapack_cpld_jtag_shift_dr(16, 0xffff);
silicon_id[1] = portapack_cpld_jtag_shift_dr(16, 0xffff);
silicon_id[2] = portapack_cpld_jtag_shift_dr(16, 0xffff);
silicon_id[3] = portapack_cpld_jtag_shift_dr(16, 0xffff);
silicon_id[4] = portapack_cpld_jtag_shift_dr(16, 0xffff);
if( (silicon_id[0] != 0x8232) ||
(silicon_id[1] != 0x2aa2) ||
(silicon_id[2] != 0x4a82) ||
(silicon_id[3] != 0x8c0c) ||
(silicon_id[4] != 0x0000) ) {
return false;
}
/* Sector erase:
* Involves shifting in the instruction to erase the device and applying
* an erase pulse or pulses. The erase pulse is automatically generated
* internally by waiting in the run, test, or idle state for the
* specified erase pulse time of 500 ms for the CFM block and 500 ms for
* each sector of the user flash memory (UFM) block.
*/
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0011); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x2F2); // Erase pulse
portapack_cpld_jtag_runtest_tck(9000003); // 500ms
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0001); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x2F2); // Erase pulse
portapack_cpld_jtag_runtest_tck(9000003); // 500ms
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0000); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x2F2); // Erase pulse
portapack_cpld_jtag_runtest_tck(9000003); // 500ms
/* Program:
* involves shifting in the address, data, and program instruction and
* generating the program pulse to program the flash cells. The program
* pulse is automatically generated internally by waiting in the run/test/
* idle state for the specified program pulse time of 75 μs. This process
* is repeated for each address in the CFM and UFM blocks.
*/
/* Block 0 */
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0000); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x2F4); // Program
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_program_block(
portapack_cpld_jtag_block_0,
ARRAY_SIZE(portapack_cpld_jtag_block_0)
);
/* Block 1 */
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0001); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x2F4); // Program
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_program_block(
portapack_cpld_jtag_block_1,
ARRAY_SIZE(portapack_cpld_jtag_block_1)
);
/* Verify */
/* Block 0 */
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0000); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x205); // Read
portapack_cpld_jtag_runtest_tck(93); // 5us
const bool block_1_success = portapack_cpld_jtag_verify_block(
portapack_cpld_jtag_block_0,
ARRAY_SIZE(portapack_cpld_jtag_block_0)
);
/* Block 1 */
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5us
portapack_cpld_jtag_shift_dr(13, 0x0001); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x205); // Read
portapack_cpld_jtag_runtest_tck(93); // 5us
const bool block_2_success = portapack_cpld_jtag_verify_block(
portapack_cpld_jtag_block_1,
ARRAY_SIZE(portapack_cpld_jtag_block_1)
);
/* Do "something". Not sure what, but it happens after verify. */
/* Starts with a sequence the same as Program: Block 0. */
/* Perhaps it is a write to tell the CPLD that the bitstream
* verified OK, and it's OK to load and execute? And despite only
* one bit changing, a write must be a multiple of a particular
* length (64 bits)? */
portapack_cpld_jtag_shift_ir(10, 0x203); // Sector select
portapack_cpld_jtag_runtest_tck(93); // 5 us
portapack_cpld_jtag_shift_dr(13, 0x0000); // Sector ID
portapack_cpld_jtag_shift_ir(10, 0x2F4); // Program
portapack_cpld_jtag_runtest_tck(93); // 5 us
/* TODO: Use data from cpld_block_0, with appropriate bit(s) changed */
/* Perhaps this is the "ISP_DONE" bit? */
portapack_cpld_jtag_shift_dr(16, 0x7BFF);
portapack_cpld_jtag_runtest_tck(1800); // 100us
portapack_cpld_jtag_shift_dr(16, 0xFFFF);
portapack_cpld_jtag_runtest_tck(1800); // 100us
portapack_cpld_jtag_shift_dr(16, 0xBFFC);
portapack_cpld_jtag_runtest_tck(1800); // 100us
portapack_cpld_jtag_shift_dr(16, 0xF9E7);
portapack_cpld_jtag_runtest_tck(1800); // 100us
/* Exit ISP? Reset? */
portapack_cpld_jtag_shift_ir(10, 0x201);
portapack_cpld_jtag_runtest_tck(18003); // 1ms
portapack_cpld_jtag_shift_ir(10, 0x3FF);
portapack_cpld_jtag_runtest_tck(18000); // 1ms
if( (block_1_success == false) ||
(block_2_success == false) ) {
return false;
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment