/// <summary> /// Program the target device with the provided hexfile /// </summary> /// <param name="hexFile">Hexfile containing data to program</param> /// <param name="programConfigs">If true, will attempt to program config words (WARNING: programming invalid config words could brick the device!)</param> public override void Program(HexFile hexFile, bool programConfigs = false) { // Program config words first to minimise the risk that the MCU // is reset during programming, thus leaving the MCU in a state // that can't be booted. if (programConfigs) { var configRegions = memoryRegions.Where(r => r.Type == MemoryRegionType.CONFIG); // Not all devices provide CONFIG memory regions, as it is usually not desirable to program them anyway. if (configRegions.Count() == 0) { throw new BootloaderException("Cannot program config words for this device (No CONFIG memory regions)"); } foreach (var memoryRegion in configRegions) { ProgramMemoryRegion(hexFile, memoryRegion); } } // Program everything else (PROGMEM, EEDATA) var dataRegions = memoryRegions.Where(r => r.Type != MemoryRegionType.CONFIG); // This shouldn't happen in a properly configured device, but show in case it does to prevent confusion if (dataRegions.Count() == 0) { throw new BootloaderException("Cannot program memory (No PROGMEM/EEDATA memory regions)"); } foreach (var memoryRegion in dataRegions) { ProgramMemoryRegion(hexFile, memoryRegion); } }
static void Main(string[] argv) { try { Args args = new Args(); args.protocol = ProtocolType.HID; args.hexfile = null; #region Command Line Arguments var p_main = new OptionSet() { //{ "v|verbose", v => args.verbose = (v != null) }, //{ "d|debug", "Show debug information", v => args.debug = (v != null)}, { "h|help", v => args.showhelp = (v != null) }, { "n|no-verify", "Don't verify on program", v => args.noVerify = (v != null) }, { "c|program-configs", "Program configuration bits", v => args.programConfigs = (v != null) }, { "r|reset", "Reset device on completion", v => args.autoReset = (v != null) }, // Protocol selection /*{ "auto", "Automatically scan for devices (default)", v=> {if (v!=null) args.protocol = ProtocolType.Auto;}}, * { "hid", "Use USB HID bootloader", v=> {if (v!=null) args.protocol = ProtocolType.HID;}}, * { "tcp", "Use TCP network bootloader", v=> {if (v!=null) args.protocol = ProtocolType.TCP;}}, * { "udp", "Use UDP network bootlaoder", v=> {if (v!=null) args.protocol = ProtocolType.UDP;}}, * { "serial", "Use Serial bootloader", v=> {if (v!=null) args.protocol = ProtocolType.Serial;}},*/ }; var p_hid = new OptionSet() { //{ "default", v=>{}}, { "device=", "Optional VID/PID of the USB Device\neg. VID_04D8&PID_003C (default value)", v => args.hid.deviceId = v }, }; /*var p_network = new OptionSet() { * { "addr=", "IP Address", v => args.network.address = v }, * { "port=", "Port", (int v) => args.network.port = v }, * }; * var p_serial = new OptionSet() { * { "port=", "COM Port", v => args.serial.port = v}, * { "baud=", "Baud Rate", (int v) => args.serial.baud = v}, * // TODO: could add an option for multiple serial protocols? * };*/ #endregion List <string> subargs = p_main.Parse(argv); List <string> extra = subargs; #region Console Help if (args.showhelp) // --help { ShowHelp(p_main, p_hid); return; } #endregion #region Auto Protocol Scan // Try and search for devices automatically. // Order of search is: USB-HID, Serial, TCP, UDP // USB-HID uses the default DeviceID, // Serial uses common baud rates on all connected COM ports, // TCP/UDP looks for a broadcast from the device. if (args.protocol == ProtocolType.Auto) { args.protocol = AutoProtocolScan(); } #endregion #region Protocol Specific Argument Parsing // Parse protocol specific arguments for the specified protocol. // Does not apply for automatic scan, as parameters are determined automatically. // Also performs some basic verification of the parameters before doing anything with them. switch (args.protocol) { case ProtocolType.HID: extra = p_hid.Parse(subargs); // Validate Device ID format if (args.hid.deviceId != null) { if (!Regex.Match(args.hid.deviceId, @"vid_[0-9a-f]{4}&pid_[0-9a-f]{4}", RegexOptions.IgnoreCase).Success) { throw new OptionException(String.Format("Invalid USB-HID Device ID '{0}'", args.hid.deviceId), "device"); } } break; case ProtocolType.TCP: case ProtocolType.UDP: throw new NotImplementedException("TCP/UDP Bootloader is not implemented yet"); //extra = p_network.Parse(subargs); //TODO: validate IP address/port if specified break; case ProtocolType.Serial: throw new NotImplementedException("Serial Bootloader is not implemented yet"); //extra = p_serial.Parse(subargs); //TODO: validate COM port and baud rate if specified //TODO: allow an auto com port? break; } #endregion #region Un-named Argument Parsing // There are two un-named arguments, the first is the action to perform, // the second is a filename for the hex file to use (required for program/verify/read actions) // Must contain at least one extra parameter (action) if (extra.Count < 1) { throw new OptionException("Must specify an action", "action"); } args.action = (ProgrammerAction)Enum.Parse(typeof(ProgrammerAction), extra[0], true); // May contain a hexfile parameter (required for certain actions) if (args.action == ProgrammerAction.Program || args.action == ProgrammerAction.Read || args.action == ProgrammerAction.Verify) { if (extra.Count < 2) { throw new OptionException("Must specify a hex file for this action", "hexfile"); } args.hexfile = extra[1]; } #endregion // Obtain a bootloader object for the given protocol, using polymorphism Bootloader bootloader = GetBootloaderObject(args); // Make sure the device is responding, and query it for device parameters bootloader.Query(); Console.WriteLine("Found device"); #region Action Execution // Run the specified action switch (args.action) { case ProgrammerAction.Scan: // Do nothing, as we need to scan for the device anyway. //TODO: Show address/port/baud/etc. if auto-scanning break; case ProgrammerAction.Erase: Console.WriteLine("Erasing program memory"); bootloader.Erase(); break; case ProgrammerAction.Read: { Console.WriteLine("Reading program memory"); HexFile hexfile = bootloader.Read(); Console.WriteLine("Saved to '{0}'", args.hexfile); //TODO: save hexfile } break; case ProgrammerAction.Program: { HexFile hexfile = new HexFile(args.hexfile); Console.WriteLine("Loaded HEX File '{0}' ({1} bytes)", args.hexfile, hexfile.Size); if (hexfile.Size == 0) { throw new BootloaderException("Hex file is empty"); } // Erase the device before programming (required) Console.WriteLine("Erasing"); bootloader.Erase(); // Program the program memory, and optionally the config bits Console.WriteLine("Programming"); bootloader.Program(hexfile, args.programConfigs); // Optionally verify it was programmed correctly if (!args.noVerify) { Console.WriteLine("Verifying"); bootloader.Verify(hexfile); } Console.WriteLine("Signing"); bootloader.SignFlash(); } break; case ProgrammerAction.Verify: { HexFile hexfile = new HexFile(args.hexfile); Console.WriteLine("Verifying against '{0}'", args.hexfile); bootloader.Verify(hexfile); } break; case ProgrammerAction.Reset: Console.WriteLine("Resetting device"); bootloader.Reset(); break; case ProgrammerAction.Run: Console.WriteLine("Running"); bootloader.Reset(); // TODO: implement break; } // Reset after completing command if (args.autoReset && args.action != ProgrammerAction.Reset && args.action != ProgrammerAction.Run) { Console.WriteLine("Resetting device"); bootloader.Reset(); } #endregion //Console.WriteLine("Done."); } #if !DEBUG /*catch (HidDeviceException e) * { * Console.Error.WriteLine("HID Error: {0}", e.Message); * }*/ catch (BootloaderException e) { Console.Error.WriteLine("Error: {0}", e.Message); } catch (OptionException e) { Console.Error.WriteLine("{0}", e.Message); } catch (Exception e) { Console.Error.WriteLine(); Console.Error.WriteLine(e); Console.Error.WriteLine(); } #endif finally { #if DEBUG // Pause the console so we can see the output Console.ReadKey(); #endif } }
public abstract void Program(HexFile hex, bool programConfigs = false);
public abstract void Verify(HexFile hex);
/// <summary> /// Program the target PIC memory region using the provided hex file /// </summary> /// <param name="hexFile">Hexfile containing data to program</param> /// <param name="memoryRegion">The target memory region to program</param> private void ProgramMemoryRegion(HexFile hexFile, MemoryRegionStruct memoryRegion) { using (var WriteFile = HidDevice.GetWriteFile()) { byte currentByteInAddress = 1; bool skippedBlock = false; // Obtain the data related to the current memory region var regionData = hexFile.GetMemoryRegion(memoryRegion.Address, memoryRegion.Size, bytesPerAddress); int j = 0; // While the current address is less than the end address uint address = memoryRegion.Address; uint endAddress = memoryRegion.Address + memoryRegion.Size; while (address < endAddress) { // Prepare command ProgramDeviceStruct myCommand = new ProgramDeviceStruct { WindowsReserved = 0, Command = PROGRAM_DEVICE, Address = address }; myCommand.Data = new byte[PROGRAM_PACKET_DATA_SIZE]; // If a block consists of all 0xFF, then there is no need to write the block // as the erase cycle will have set everything to 0xFF bool skipBlock = true; byte i; for (i = 0; i < bytesPerPacket; i++) { byte data = regionData[j++]; myCommand.Data[i + (myCommand.Data.Length - bytesPerPacket)] = data; if (data != 0xFF) { // We can skip a block if all bytes are 0xFF. // Bytes are also ignored if it is byte 4 of a 3 word instruction on PIC24 (bytesPerAddress=2, currentByteInAddress=2, even address) if ((bytesPerAddress != 2) || ((address % 2) == 0) || (currentByteInAddress != 2)) { // Then we can't skip this block of data skipBlock = false; } } if (currentByteInAddress == bytesPerAddress) { // If we haven't written enough bytes per address to be at the next address address++; currentByteInAddress = 1; } else { // If we haven't written enough bytes to fill this address currentByteInAddress++; } //If we have reached the end of the memory region, then we // need to pad the data at the end of the packet instead // of the front of the packet so we need to shift the data // to the back of the packet. if (address >= endAddress) { byte n; i++; int len = myCommand.Data.Length; for (n = 0; n < len; n++) { if (n < i) { // Move it from where it is to the back of the packet, thus shifting all of the data down. myCommand.Data[len - n - 1] = myCommand.Data[i + (len - bytesPerPacket) - n - 1]; } else { myCommand.Data[len - n - 1] = 0x00; } } // Break out of the for loop now that all the data has been padded out. break; } }//end for // Use the counter to determine how many bytes were written myCommand.BytesPerPacket = i; //If the block was all 0xFF then we can just skip actually programming // this device. Otherwise enter the programming sequence if (!skipBlock) { //If we skipped one block before this block then we may need // to send a proramming complete command to the device before // sending the data for this command. if (skippedBlock) { SendCommandPacket <BootloaderCommandStruct>(new BootloaderCommandStruct { WindowsReserved = 0, Command = PROGRAM_COMPLETE }); //since we have now indicated that the programming is complete // then we now mark that we haven't skipped any blocks skippedBlock = false; } // Write the packet data! /*string debug = ""; * foreach (byte b in myCommand.Data) * debug += b.ToString("x2") + " "; * Console.WriteLine(">>> USB OUT Packet >>>\n{0}", debug);*/ SendCommandPacket <ProgramDeviceStruct>(myCommand); } else { // We are skipping the block skippedBlock = true; } }//end while // All data for this region has been programmed SendCommandPacket <BootloaderCommandStruct>(new BootloaderCommandStruct { WindowsReserved = 0, Command = PROGRAM_COMPLETE }); }//end using }
public override void Verify(HexFile hex) { //throw new BootloaderException("Invalid byte at 0x{0:x}", 0); }
static void Main(string[] argv) { try { Args args = new Args(); args.protocol = ProtocolType.HID; args.hexfile = null; #region Command Line Arguments var p_main = new OptionSet() { //{ "v|verbose", v => args.verbose = (v != null) }, //{ "d|debug", "Show debug information", v => args.debug = (v != null)}, { "h|help", v => args.showhelp = (v != null) }, { "n|no-verify", "Don't verify on program", v => args.noVerify = (v != null)}, { "c|program-configs", "Program configuration bits", v => args.programConfigs = (v != null)}, { "r|reset", "Reset device on completion", v => args.autoReset = (v != null)}, // Protocol selection /*{ "auto", "Automatically scan for devices (default)", v=> {if (v!=null) args.protocol = ProtocolType.Auto;}}, { "hid", "Use USB HID bootloader", v=> {if (v!=null) args.protocol = ProtocolType.HID;}}, { "tcp", "Use TCP network bootloader", v=> {if (v!=null) args.protocol = ProtocolType.TCP;}}, { "udp", "Use UDP network bootlaoder", v=> {if (v!=null) args.protocol = ProtocolType.UDP;}}, { "serial", "Use Serial bootloader", v=> {if (v!=null) args.protocol = ProtocolType.Serial;}},*/ }; var p_hid = new OptionSet() { //{ "default", v=>{}}, { "device=", "Optional VID/PID of the USB Device\neg. VID_04D8&PID_003C (default value)", v => args.hid.deviceId = v}, }; /*var p_network = new OptionSet() { { "addr=", "IP Address", v => args.network.address = v }, { "port=", "Port", (int v) => args.network.port = v }, }; var p_serial = new OptionSet() { { "port=", "COM Port", v => args.serial.port = v}, { "baud=", "Baud Rate", (int v) => args.serial.baud = v}, // TODO: could add an option for multiple serial protocols? };*/ #endregion List<string> subargs = p_main.Parse(argv); List<string> extra = subargs; #region Console Help if (args.showhelp) // --help { ShowHelp(p_main, p_hid); return; } #endregion #region Auto Protocol Scan // Try and search for devices automatically. // Order of search is: USB-HID, Serial, TCP, UDP // USB-HID uses the default DeviceID, // Serial uses common baud rates on all connected COM ports, // TCP/UDP looks for a broadcast from the device. if (args.protocol == ProtocolType.Auto) { args.protocol = AutoProtocolScan(); } #endregion #region Protocol Specific Argument Parsing // Parse protocol specific arguments for the specified protocol. // Does not apply for automatic scan, as parameters are determined automatically. // Also performs some basic verification of the parameters before doing anything with them. switch (args.protocol) { case ProtocolType.HID: extra = p_hid.Parse(subargs); // Validate Device ID format if (args.hid.deviceId != null) { if (!Regex.Match(args.hid.deviceId, @"vid_[0-9a-f]{4}&pid_[0-9a-f]{4}", RegexOptions.IgnoreCase).Success) throw new OptionException(String.Format("Invalid USB-HID Device ID '{0}'", args.hid.deviceId), "device"); } break; case ProtocolType.TCP: case ProtocolType.UDP: throw new NotImplementedException("TCP/UDP Bootloader is not implemented yet"); //extra = p_network.Parse(subargs); //TODO: validate IP address/port if specified break; case ProtocolType.Serial: throw new NotImplementedException("Serial Bootloader is not implemented yet"); //extra = p_serial.Parse(subargs); //TODO: validate COM port and baud rate if specified //TODO: allow an auto com port? break; } #endregion #region Un-named Argument Parsing // There are two un-named arguments, the first is the action to perform, // the second is a filename for the hex file to use (required for program/verify/read actions) // Must contain at least one extra parameter (action) if (extra.Count < 1) throw new OptionException("Must specify an action", "action"); args.action = (ProgrammerAction)Enum.Parse(typeof(ProgrammerAction), extra[0], true); // May contain a hexfile parameter (required for certain actions) if (args.action == ProgrammerAction.Program || args.action == ProgrammerAction.Read || args.action == ProgrammerAction.Verify) { if (extra.Count < 2) throw new OptionException("Must specify a hex file for this action", "hexfile"); args.hexfile = extra[1]; } #endregion // Obtain a bootloader object for the given protocol, using polymorphism Bootloader bootloader = GetBootloaderObject(args); // Make sure the device is responding, and query it for device parameters bootloader.Query(); Console.WriteLine("Found device"); #region Action Execution // Run the specified action switch (args.action) { case ProgrammerAction.Scan: // Do nothing, as we need to scan for the device anyway. //TODO: Show address/port/baud/etc. if auto-scanning break; case ProgrammerAction.Erase: Console.WriteLine("Erasing program memory"); bootloader.Erase(); break; case ProgrammerAction.Read: { Console.WriteLine("Reading program memory"); HexFile hexfile = bootloader.Read(); Console.WriteLine("Saved to '{0}'", args.hexfile); //TODO: save hexfile } break; case ProgrammerAction.Program: { HexFile hexfile = new HexFile(args.hexfile); Console.WriteLine("Loaded HEX File '{0}' ({1} bytes)", args.hexfile, hexfile.Size); if (hexfile.Size == 0) throw new BootloaderException("Hex file is empty"); // Erase the device before programming (required) Console.WriteLine("Erasing"); bootloader.Erase(); // Program the program memory, and optionally the config bits Console.WriteLine("Programming"); bootloader.Program(hexfile, args.programConfigs); // Optionally verify it was programmed correctly if (!args.noVerify) { Console.WriteLine("Verifying"); bootloader.Verify(hexfile); } } break; case ProgrammerAction.Verify: { HexFile hexfile = new HexFile(args.hexfile); Console.WriteLine("Verifying against '{0}'", args.hexfile); bootloader.Verify(hexfile); } break; case ProgrammerAction.Reset: Console.WriteLine("Resetting device"); bootloader.Reset(); break; case ProgrammerAction.Run: Console.WriteLine("Running"); bootloader.Reset(); // TODO: implement break; } // Reset after completing command if (args.autoReset && args.action != ProgrammerAction.Reset && args.action != ProgrammerAction.Run) { Console.WriteLine("Resetting device"); bootloader.Reset(); } #endregion //Console.WriteLine("Done."); } #if !DEBUG /*catch (HidDeviceException e) { Console.Error.WriteLine("HID Error: {0}", e.Message); }*/ catch (BootloaderException e) { Console.Error.WriteLine("Error: {0}", e.Message); } catch (OptionException e) { Console.Error.WriteLine("{0}", e.Message); } catch (Exception e) { Console.Error.WriteLine(); Console.Error.WriteLine(e); Console.Error.WriteLine(); } #endif finally { #if DEBUG // Pause the console so we can see the output Console.ReadKey(); #endif } }