456 lines
16 KiB
C#
456 lines
16 KiB
C#
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.ComponentModel.Design;
|
|
using System.Data;
|
|
using System.IO.Ports;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices.ComTypes;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Windows.Forms;
|
|
|
|
namespace TinyFPGA_VisualProgrammer
|
|
{
|
|
public class JSON_BoardMeta
|
|
{
|
|
[JsonProperty("name")]
|
|
public string Name { get; set; }
|
|
|
|
[JsonProperty("fpga")]
|
|
public string FPGA { get; set; }
|
|
|
|
[JsonProperty("hver")]
|
|
public string HVer { get; set; }
|
|
|
|
[JsonProperty("uuid")]
|
|
public string UUID { get; set; }
|
|
}
|
|
|
|
public class JSON_BootMeta
|
|
{
|
|
[JsonProperty("bootloader")]
|
|
public string BootLoader { get; set; }
|
|
|
|
[JsonProperty("bver")]
|
|
public string BVer { get; set; }
|
|
|
|
[JsonProperty("update")]
|
|
public string Update { get; set; }
|
|
|
|
[JsonProperty("addrmap")]
|
|
public JSON_AddrMap AddrMap { get; set; }
|
|
}
|
|
|
|
public class JSON_AddrMap
|
|
{
|
|
[JsonProperty("bootloader")]
|
|
public string BootLoader { get; set; }
|
|
|
|
[JsonProperty("userimage")]
|
|
public string UserImage { get; set; }
|
|
|
|
[JsonProperty("userdata")]
|
|
public string UserData { get; set; }
|
|
}
|
|
|
|
public class TinyProg
|
|
{
|
|
SerialController pPort = null;
|
|
BackgroundWorker bw = null;
|
|
Label status = null;
|
|
byte SecurePage_bitOffset = 0x00;
|
|
byte SecurePage_writeCmd = 0x00;
|
|
byte SecurePage_readCmd = 0x00;
|
|
byte SecurePage_eraseCmd = 0x00;
|
|
TinyMeta tinyMeta = null;
|
|
short step_prog = 0;
|
|
|
|
public bool TinyFPGAIsConnected { get; private set; }
|
|
|
|
public TinyProg(SerialController p, Label sl, BackgroundWorker _bw)
|
|
{
|
|
TinyFPGAIsConnected = false;
|
|
if (p != null)
|
|
pPort = p;
|
|
if (sl != null)
|
|
status = sl;
|
|
if (_bw != null)
|
|
bw = _bw;
|
|
}
|
|
|
|
|
|
public void CheckTinyPresence()
|
|
{
|
|
WakeCmd();
|
|
byte[] flash_id = RetrieveFlashID();
|
|
if (flash_id.Length >= 3)
|
|
{
|
|
if (flash_id[0] == 0x9D && flash_id[2] == 0x60)
|
|
{
|
|
// ISSI
|
|
SecurePage_bitOffset = 4;
|
|
SecurePage_writeCmd = 0x62;
|
|
SecurePage_readCmd = 0x68;
|
|
SecurePage_eraseCmd = 0x64;
|
|
}
|
|
else
|
|
{
|
|
// Adesto
|
|
SecurePage_bitOffset = 0;
|
|
SecurePage_writeCmd = 0x42;
|
|
SecurePage_readCmd = 0x48;
|
|
SecurePage_eraseCmd = 0x44;
|
|
}
|
|
}
|
|
|
|
tinyMeta = new TinyMeta(this);
|
|
if (tinyMeta.BoardMeta != null)
|
|
TinyFPGAIsConnected = true;
|
|
else
|
|
TinyFPGAIsConnected = false;
|
|
}
|
|
|
|
public JSON_BoardMeta GetBoardData()
|
|
{
|
|
return tinyMeta.BoardMeta;
|
|
}
|
|
|
|
public JSON_BootMeta GetBootData()
|
|
{
|
|
return tinyMeta.BootMeta;
|
|
}
|
|
|
|
byte[] SendCommand(byte opcode, byte[] addr, byte[] datas, uint read_len = 0)
|
|
{
|
|
if (addr == null)
|
|
addr = new byte[] { };
|
|
else
|
|
addr = addr.Take(3).Reverse().ToArray(); // Drop the unused MSB 8b
|
|
|
|
if (datas == null)
|
|
datas = new byte[] { };
|
|
|
|
byte[] _ret = new byte[read_len];
|
|
byte[] cmdSequence = new byte[sizeof(byte) + sizeof(ushort) + sizeof(ushort) + sizeof(byte) + addr.Length + datas.Length];
|
|
int i;
|
|
|
|
/*
|
|
* Prepare datas container
|
|
*/
|
|
cmdSequence[0] = 0x01;
|
|
|
|
ushort seqSize = (ushort)(1 + addr.Length + datas.Length);
|
|
cmdSequence[1] = (byte)(seqSize & 0xFF); // LSB - Little-endian
|
|
cmdSequence[2] = (byte)((seqSize >> 8) & 0xFF);
|
|
|
|
cmdSequence[3] = (byte)(read_len & 0xFF); // LSB - Little-endian
|
|
cmdSequence[4] = (byte)((read_len >> 8) & 0xFF);
|
|
|
|
cmdSequence[5] = opcode;
|
|
i = 6;
|
|
foreach (byte ab in addr)
|
|
cmdSequence[i++] = ab; // Caution - this write in reverse order (big-endian)
|
|
i = 6 + addr.Length;
|
|
foreach (byte d in datas)
|
|
cmdSequence[i++] = d; // Caution - this write in reverse order (big-endian)
|
|
|
|
/*
|
|
* Send datas container through serial line
|
|
*/
|
|
//pPort.clearBuffer();
|
|
pPort.SendBytes(cmdSequence, cmdSequence.Length);
|
|
|
|
if (read_len > 0)
|
|
_ret = pPort.WaitForBytes((int)read_len);
|
|
|
|
return _ret;
|
|
}
|
|
|
|
public void WakeCmd()
|
|
{
|
|
SendCommand(0xab, null, null);
|
|
}
|
|
|
|
public void ResetCmd()
|
|
{
|
|
pPort.SendBytes(new byte[] { 0x00 }, 1);
|
|
}
|
|
|
|
byte[] RetrieveFlashID()
|
|
{
|
|
return SendCommand(0x9f, null, null, 3);
|
|
}
|
|
|
|
void FlashWriteEnable()
|
|
{
|
|
SendCommand(0x06, null, null);
|
|
}
|
|
|
|
void FlashWriteDisable()
|
|
{
|
|
SendCommand(0x04, null, null);
|
|
}
|
|
|
|
byte RetrieveStatus()
|
|
{
|
|
return SendCommand(0x05, null, null, 1)[0];
|
|
}
|
|
|
|
void WaitWhileBusy()
|
|
{
|
|
while ((RetrieveStatus() & 1) == 1) { }
|
|
}
|
|
|
|
void EraseSecureRegPage(uint page)
|
|
{
|
|
FlashWriteEnable();
|
|
SendCommand(SecurePage_eraseCmd, BitConverter.GetBytes((page & 0xFFFF) << 8 + SecurePage_bitOffset), null);
|
|
WaitWhileBusy();
|
|
}
|
|
|
|
void ProgramSecureRegPage(uint page, byte[] data)
|
|
{
|
|
FlashWriteEnable();
|
|
SendCommand(SecurePage_writeCmd, BitConverter.GetBytes((page & 0xFFFF) << 8 + SecurePage_bitOffset), data);
|
|
WaitWhileBusy();
|
|
}
|
|
|
|
public string ReadSecureRegPage(uint page)
|
|
{
|
|
byte[] cmdResult = SendCommand(SecurePage_readCmd, BitConverter.GetBytes((page & 0xFFFF) << 8 + SecurePage_bitOffset), new byte[] { 0x00 }, 255);
|
|
return Encoding.ASCII.GetString(cmdResult).Trim(); // Need trimming to clear string from dirty bytes from the casting process.
|
|
}
|
|
|
|
internal byte[] FlashRead(uint addr, uint length, bool progress = false)
|
|
{
|
|
byte[] datas = null;
|
|
uint initial_length = length;
|
|
uint readen_bytes = 0;
|
|
string default_status = status.Text;
|
|
|
|
while (length > 0)
|
|
{
|
|
uint readLength = Math.Min(255, length);
|
|
if (datas == null)
|
|
datas = SendCommand(0x0B, BitConverter.GetBytes(addr + readen_bytes), new byte[] {0x00}, readLength);
|
|
else
|
|
datas = datas.Concat(SendCommand(0x0B, BitConverter.GetBytes(addr + readen_bytes), new byte[] { 0x00 }, readLength)).ToArray();
|
|
|
|
readen_bytes += readLength;
|
|
length -= readLength;
|
|
if (step_prog == 3)
|
|
{
|
|
status.Invoke((MethodInvoker)(() => status.Text = "Verifying " + readen_bytes.ToString() + " of " + initial_length + " bytes..."));
|
|
bw.ReportProgress(850 + Convert.ToInt32((float)readen_bytes / initial_length * 150));
|
|
}
|
|
}
|
|
|
|
return datas;
|
|
}
|
|
|
|
internal void FlashWrite(uint addr, byte[] datas)
|
|
{
|
|
uint initial_length = Convert.ToUInt32(datas.Length);
|
|
uint written_bytes = 0;
|
|
//string default_status = status.Text;
|
|
|
|
while (written_bytes < datas.Length)
|
|
{
|
|
uint Addr256bBoundary = 256 - ((addr + written_bytes) & 0xFF);
|
|
uint writeLength = Math.Min(256, Math.Min(Convert.ToUInt32(datas.Length) - written_bytes, Addr256bBoundary));
|
|
|
|
SendCommand(0x06, null, null); // Write enable
|
|
SendCommand(0x02, BitConverter.GetBytes(addr + written_bytes), datas.Skip(Convert.ToInt32(written_bytes)).Take(Convert.ToInt32(writeLength)).ToArray());
|
|
WaitWhenBusy();
|
|
|
|
written_bytes += writeLength;
|
|
if (step_prog == 2) {
|
|
status.Invoke((MethodInvoker)(() => status.Text = "Writing " + written_bytes.ToString() + " of " + initial_length + " bytes..."));
|
|
bw.ReportProgress(150 + Convert.ToInt32((float)written_bytes / initial_length * 700));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void WaitWhenBusy()
|
|
{
|
|
//Thread.Sleep(1);
|
|
while ((SendCommand(0x05, null, null, 1)[0] & 1) != 0)
|
|
//Thread.Sleep(1);
|
|
continue;
|
|
}
|
|
|
|
private void FlashErase(uint addr, uint length)
|
|
{
|
|
byte OpCode;
|
|
switch (length)
|
|
{
|
|
default:
|
|
case 4 * 1024:
|
|
OpCode = 0x20;
|
|
break;
|
|
case 32 * 1024:
|
|
OpCode = 0x52;
|
|
break;
|
|
case 64 * 1024:
|
|
OpCode = 0xD8;
|
|
break;
|
|
}
|
|
|
|
SendCommand(0x06, null, null); // Write enable
|
|
SendCommand(OpCode, BitConverter.GetBytes(addr), null);
|
|
WaitWhenBusy();
|
|
}
|
|
|
|
internal void Erase(uint addr, uint length)
|
|
{
|
|
//uint initial_length = length;
|
|
uint[] validLength = {1, 4 * 1024, 32 * 1024, 64 * 1024};
|
|
uint erase_length = 0;
|
|
byte[] start_read_data = null;
|
|
byte[] end_read_data = null;
|
|
|
|
while (length > 0)
|
|
{
|
|
erase_length = validLength.Where(p => p <= length && addr % p == 0).Max();
|
|
|
|
if (erase_length == 1)
|
|
{
|
|
// start_addr end_addr
|
|
// v v
|
|
// +------------------+------------------+----------------+
|
|
// | keep | erase | keep |
|
|
// +------------------+------------------+----------------+
|
|
// <- start_length -> <- erase_length -> <- end_length ->
|
|
|
|
uint start_addr = addr & 0xFFF000;
|
|
uint start_length = addr & 0xFFF;
|
|
erase_length = Math.Min(0x1000 - start_length, length);
|
|
uint end_addr = start_addr + start_length + erase_length;
|
|
uint end_length = start_addr + 0x1000 - end_addr;
|
|
|
|
// Readback data that should be rewritten
|
|
if (start_length > 0)
|
|
start_read_data = FlashRead(start_addr, start_length);
|
|
if (end_length > 0)
|
|
end_read_data = FlashRead(end_addr, end_length);
|
|
|
|
FlashErase(start_addr, 0x1000);
|
|
|
|
// Rewrite data
|
|
if (start_length > 0)
|
|
FlashWrite(start_addr, start_read_data);
|
|
if (end_length > 0)
|
|
FlashWrite(end_addr, end_read_data);
|
|
}
|
|
else
|
|
FlashErase(addr, erase_length);
|
|
|
|
length -= erase_length;
|
|
addr += erase_length;
|
|
/*if (step_prog == 1)
|
|
{
|
|
uint a = Convert.ToUInt32(initial_length - (-initial_length + length));
|
|
status.Invoke((MethodInvoker)(() => status.Text = "Erasing " + a.ToString() + " of " + initial_length + " bytes..."));
|
|
bw.ReportProgress(Convert.ToInt32((float)1 / initial_length * 15));
|
|
}*/
|
|
}
|
|
}
|
|
|
|
internal bool Program(ref byte[] bitstream, ref BackgroundWorker bw)
|
|
{
|
|
GroupCollection usrimg_addrs = Regex.Match(GetBootData().AddrMap.UserImage, @"^\s*(?<start>0x[A-Fa-f0-9]+)\s*-\s*(?<end>0x[A-Fa-f0-9]+)\s*$").Groups;
|
|
uint[] usrimg_boundings = { Convert.ToUInt32(usrimg_addrs["start"].Value, 16), Convert.ToUInt32(usrimg_addrs["end"].Value, 16) };
|
|
|
|
if (bitstream.Length > (usrimg_boundings[1] - usrimg_boundings[0]))
|
|
{
|
|
status.Invoke((MethodInvoker)(() => status.Text = "Bitstream is too large for the target!"));
|
|
return false;
|
|
}
|
|
|
|
// Erase the flash
|
|
step_prog = 1;
|
|
status.Invoke((MethodInvoker)(() => status.Text = "Erasing..."));
|
|
Erase(usrimg_boundings[0], Convert.ToUInt32(bitstream.Length));
|
|
|
|
// Program the bitstream
|
|
step_prog = 2;
|
|
FlashWrite(usrimg_boundings[0], bitstream);
|
|
//SendCommand(0x04, null, null); // Write disable
|
|
|
|
// Check the flash datas
|
|
step_prog = 3;
|
|
byte[] readback = FlashRead(usrimg_boundings[0], Convert.ToUInt32(bitstream.Length));
|
|
|
|
step_prog = 0;
|
|
bw.ReportProgress(1000);
|
|
if (bitstream.SequenceEqual(bitstream))
|
|
{
|
|
ResetCmd();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public class TinyMeta
|
|
{
|
|
readonly TinyProg tProg;
|
|
//JSONRoot JSON_root;
|
|
public JSON_BoardMeta BoardMeta { get; private set; }
|
|
public JSON_BootMeta BootMeta { get; private set; }
|
|
|
|
public TinyMeta(TinyProg tProg)
|
|
{
|
|
this.tProg = tProg;
|
|
tProg.WakeCmd();
|
|
ReadMetadata();
|
|
}
|
|
|
|
void ReadMetadata()
|
|
{
|
|
string rawJSON;
|
|
|
|
//rawJSON = tProg.ReadSecureRegPage(1).Substring(12);
|
|
//rawJSON = rawJSON.Remove(rawJSON.Length - 2);
|
|
rawJSON = tProg.ReadSecureRegPage(1);
|
|
rawJSON = Regex.Replace(rawJSON, @"\x00", string.Empty);
|
|
rawJSON = Regex.Replace(rawJSON, @"{\x22boardmeta\x22:", string.Empty);
|
|
rawJSON = Regex.Replace(rawJSON, @"\}(?!.*\})", string.Empty);
|
|
try
|
|
{
|
|
BoardMeta = JsonConvert.DeserializeObject<JSON_BoardMeta>(rawJSON); // Read 1st register, contain boardmeta
|
|
}
|
|
catch (JsonReaderException _jex)
|
|
{
|
|
Console.WriteLine(_jex.Message);
|
|
Console.WriteLine(rawJSON);
|
|
}
|
|
Thread.Sleep(200);
|
|
|
|
//rawJSON = tProg.ReadSecureRegPage(2).Substring(11);
|
|
//rawJSON = rawJSON.Remove(rawJSON.Length - 2);
|
|
rawJSON = tProg.ReadSecureRegPage(2);
|
|
rawJSON = Regex.Replace(rawJSON, @"\x00", string.Empty);
|
|
rawJSON = Regex.Replace(rawJSON, @"{\x22bootmeta\x22:", string.Empty);
|
|
rawJSON = Regex.Replace(rawJSON, @"\}(?!.*\})", string.Empty);
|
|
try
|
|
{
|
|
BootMeta = JsonConvert.DeserializeObject<JSON_BootMeta>(rawJSON); // Read 2nd register, contain bootmeta
|
|
}
|
|
catch (JsonReaderException _jex)
|
|
{
|
|
Console.WriteLine(_jex.Message);
|
|
Console.WriteLine(rawJSON);
|
|
}
|
|
Thread.Sleep(200);
|
|
//tProg.ReadSecureRegPage(3); // Read 3rd register, contain nothing
|
|
|
|
//TODO: Read from internal memory ([17, 18, 19, 20, 21, 22, 23, 24])
|
|
}
|
|
}
|
|
}
|