/* * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. * * This file is part of PortaPack. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA. */ #include "cpld_max5.hpp" #include "jtag.hpp" #include #include namespace cpld { namespace max5 { void CPLD::bypass() { shift_ir(instruction_t::BYPASS); jtag.runtest_tck(18003); } void CPLD::sample() { shift_ir(instruction_t::SAMPLE); jtag.runtest_tck(93); for(size_t i=0; i<80; i++) { jtag.shift_dr(3, 0b111); } } void CPLD::sample(std::bitset<240>& value) { shift_ir(instruction_t::SAMPLE); jtag.runtest_tck(93); shift_dr(value); } void CPLD::extest(std::bitset<240>& value) { shift_ir(instruction_t::EXTEST); shift_dr(value); } void CPLD::clamp() { shift_ir(instruction_t::CLAMP); jtag.runtest_tck(93); } void CPLD::enable() { shift_ir(instruction_t::ISC_ENABLE); jtag.runtest_tck(18003); // 1ms } void CPLD::disable() { shift_ir(instruction_t::ISC_DISABLE); jtag.runtest_tck(18003); // 1ms } /* 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. */ void CPLD::bulk_erase() { erase_sector(0x0011); erase_sector(0x0001); erase_sector(0x0000); } bool CPLD::program( const std::array& block_0, const std::array& block_1 ) { bulk_erase(); /* 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. */ program_block(0x0000, block_0); program_block(0x0001, block_1); const auto verify_ok = verify(block_0, block_1); if( verify_ok ) { /* 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)? */ sector_select(0x0000); shift_ir(instruction_t::ISC_PROGRAM); 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? */ jtag.shift_dr(16, block_0[0] & 0xfbff); jtag.runtest_tck(1800); // 100us jtag.shift_dr(16, block_0[1]); jtag.runtest_tck(1800); // 100us jtag.shift_dr(16, block_0[2]); jtag.runtest_tck(1800); // 100us jtag.shift_dr(16, block_0[3]); jtag.runtest_tck(1800); // 100us } return verify_ok; } bool CPLD::verify( const std::array& block_0, const std::array& block_1 ) { /* Verify */ const auto block_0_success = verify_block(0x0000, block_0); const auto block_1_success = verify_block(0x0001, block_1); return block_0_success && block_1_success; } uint32_t CPLD::crc() { crc_t crc { 0x04c11db7, 0xffffffff, 0xffffffff }; block_crc(0, 3328, crc); block_crc(1, 512, crc); return crc.checksum(); } void CPLD::sector_select(const uint16_t id) { shift_ir(instruction_t::ISC_ADDRESS_SHIFT); jtag.runtest_tck(93); // 5us jtag.shift_dr(13, id); // Sector ID } bool CPLD::idcode_ok() { shift_ir(instruction_t::IDCODE); const auto idcode_read = jtag.shift_dr(idcode_length, 0); return (idcode_read == idcode); } std::array CPLD::read_silicon_id() { sector_select(0x0089); shift_ir(instruction_t::ISC_READ); jtag.runtest_tck(93); // 5us std::array silicon_id; silicon_id[0] = jtag.shift_dr(16, 0xffff); silicon_id[1] = jtag.shift_dr(16, 0xffff); silicon_id[2] = jtag.shift_dr(16, 0xffff); silicon_id[3] = jtag.shift_dr(16, 0xffff); silicon_id[4] = jtag.shift_dr(16, 0xffff); return silicon_id; } /* 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. */ bool CPLD::silicon_id_ok() { const auto silicon_id = read_silicon_id(); return ( (silicon_id[0] == 0x8232) && (silicon_id[1] == 0x2aa2) && (silicon_id[2] == 0x4a82) && (silicon_id[3] == 0x8c0c) && (silicon_id[4] == 0x0000) ); } uint32_t CPLD::usercode() { shift_ir(instruction_t::USERCODE); jtag.runtest_tck(93); // 5us return jtag.shift_dr(32, 0xffffffff); } void CPLD::erase_sector(const uint16_t id) { sector_select(id); shift_ir(instruction_t::ISC_ERASE); jtag.runtest_tck(9000003); // 500ms } void CPLD::program_block( const uint16_t id, const uint16_t* const data, const size_t count ) { sector_select(id); shift_ir(instruction_t::ISC_PROGRAM); jtag.runtest_tck(93); // 5us for(size_t i=0; i