/* * 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() { return (get_idcode() == idcode); } uint32_t CPLD::get_idcode() { shift_ir(instruction_t::IDCODE); return jtag.shift_dr(idcode_length, 0); } 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 < count; i++) { jtag.shift_dr(16, data[i]); jtag.runtest_tck(1800); } } void CPLD::prepare_read(uint16_t block) { sector_select(block); shift_ir(instruction_t::ISC_READ); jtag.runtest_tck(93); // 5us } uint32_t CPLD::read() { return jtag.shift_dr(16, 0xffff) & 0xfbff; } bool CPLD::verify_block( const uint16_t id, const uint16_t* const data, const size_t count) { sector_select(id); shift_ir(instruction_t::ISC_READ); jtag.runtest_tck(93); // 5us bool success = true; for (size_t i = 0; i < count; i++) { const auto from_device = jtag.shift_dr(16, 0xffff); if (from_device != data[i]) { if ((id == 0) && (i == 0)) { // Account for bit that indicates bitstream is valid. if ((from_device & 0xfbff) != (data[i] & 0xfbff)) { success = false; } } else { success = false; } } } return success; } bool CPLD::is_blank_block(const uint16_t id, const size_t count) { sector_select(id); shift_ir(instruction_t::ISC_READ); jtag.runtest_tck(93); // 5us bool success = true; for (size_t i = 0; i < count; i++) { const auto from_device = jtag.shift_dr(16, 0xffff); if (from_device != 0xffff) { success = false; } } return success; } void CPLD::block_crc(const uint16_t id, const size_t count, crc_t& crc) { sector_select(id); shift_ir(instruction_t::ISC_READ); jtag.runtest_tck(93); // 5us for (size_t i = 0; i < count; i++) { const uint16_t from_device = jtag.shift_dr(16, 0xffff); crc.process_bytes(&from_device, sizeof(from_device)); } } bool CPLD::is_blank() { const auto block_0_blank = is_blank_block(0x0000, 3328); const auto block_1_blank = is_blank_block(0x0001, 512); return block_0_blank && block_1_blank; } bool CPLD::AGM_enter_maintenance_mode() { shift_ir(instruction_t::AGM_STAGE_1); jtag.runtest_tck(100); shift_ir(instruction_t::AGM_STAGE_2); jtag.runtest_tck(100); shift_ir(instruction_t::AGM_STAGE_1); jtag.runtest_tck(100); shift_ir(instruction_t::AGM_SET_REGISTER); jtag.runtest_tck(100); jtag.shift_dr(8, 0x0); jtag.runtest_tck(100); shift_ir(instruction_t::AGM_PROGRAM); jtag.runtest_tck(100); jtag.shift_dr(32, 0x203f0044uL, 0x80000000); shift_ir(instruction_t::IDCODE); jtag.runtest_tck(100); auto idcode = jtag.shift_dr(idcode_length, 0); return idcode == 0x00025610; } void CPLD::AGM_exit_maintenance_mode() { shift_ir(instruction_t::AGM_RESET); jtag.runtest_tck(100); } void CPLD::AGM_enter_read_mode() { shift_ir(instruction_t::AGM_SET_REGISTER); jtag.runtest_tck(100); jtag.shift_dr(8, 0xf0); jtag.runtest_tck(100); shift_ir(instruction_t::AGM_READ); jtag.runtest_tck(100); } uint32_t CPLD::AGM_encode_address(uint32_t address, uint32_t trailer) { uint32_t p = trailer; for (size_t i = 0; i < 18; i++) { auto address_bit = (address >> i) & 0x01; p |= address_bit << (31 - i); } return p; } uint32_t CPLD::AGM_read(uint32_t address) { auto encoded_address = AGM_encode_address(address * 4, 0xC0); return jtag.shift_dr(32, encoded_address, 0x0); } } /* namespace max5 */ } /* namespace cpld */