portapack-mayhem/firmware/common/cpld_max5.cpp

337 lines
9.3 KiB
C++

/*
* 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 <cstdint>
#include <array>
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<uint16_t, 3328>& block_0,
const std::array<uint16_t, 512>& 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<uint16_t, 3328>& block_0,
const std::array<uint16_t, 512>& 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<uint16_t, 5> CPLD::read_silicon_id() {
sector_select(0x0089);
shift_ir(instruction_t::ISC_READ);
jtag.runtest_tck(93); // 5us
std::array<uint16_t, 5> 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 */