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*(?0x[A-Fa-f0-9]+)\s*-\s*(?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(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(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]) } } }