DuPALAnalyzer.java 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. package net.hkzlab.dupal.boardio;
  2. import java.util.ArrayList;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import net.hkzlab.devices.PALSpecs;
  6. import net.hkzlab.dupal.dupalproto.DuPALProto;
  7. import net.hkzlab.palanalisys.MacroState;
  8. import net.hkzlab.palanalisys.StateLink;
  9. import net.hkzlab.palanalisys.SubState;
  10. public class DuPALAnalyzer {
  11. private final Logger logger = LoggerFactory.getLogger(DuPALAnalyzer.class);
  12. private final MacroState[] mStates;
  13. private final DuPALManager dpm;
  14. private final PALSpecs pspecs;
  15. private int IOasOUT_Mask = -1;
  16. public DuPALAnalyzer(final DuPALManager dpm, final PALSpecs pspecs, final int IOasOUT_Mask) {
  17. this.dpm = dpm;
  18. this.pspecs = pspecs;
  19. this.IOasOUT_Mask = IOasOUT_Mask;
  20. this.mStates = new MacroState[1 << pspecs.getNumROUTPins()];
  21. logger.info("Provisioning for " +this.mStates.length+" possible MacroStates");
  22. }
  23. public DuPALAnalyzer(final DuPALManager dpm, final PALSpecs pspecs) {
  24. this(dpm, pspecs, -1);
  25. }
  26. public void startAnalisys() {
  27. logger.info("Device:" + pspecs + " known IOs? " + (IOasOUT_Mask < 0 ? "Y" : "N"));
  28. if(IOasOUT_Mask < 0) { // We need to detect the status of the IOs...
  29. IOasOUT_Mask = guessIOs(); // Try to guess whether IOs are Inputs or Outputs
  30. }
  31. internal_analisys();
  32. }
  33. private int guessIOs() {
  34. logger.info("starting...");
  35. int inmask = pspecs.getINMask() | pspecs.getIO_WRITEMask();
  36. logger.info("inmask: " + Integer.toHexString(inmask));
  37. int read, out_pins = 0;
  38. for(int idx = 0; idx <= inmask; idx++) {
  39. if((idx & ~inmask) != 0) continue; // We need to skip this round
  40. if(out_pins == pspecs.getIO_READMask()) break; // Apparently we found that all the IOs are outputs...
  41. logger.debug("run " + Integer.toHexString(idx >> 1) + " current guessed outs: 0x" + Integer.toHexString(out_pins) + " / " + Integer.toBinaryString(out_pins)+"b");
  42. for(int i_idx = 0; i_idx <= inmask; i_idx++) {
  43. if((i_idx & ~inmask) != 0) continue; // We need to skip this round
  44. if(out_pins == pspecs.getIO_READMask()) break; // Stop checking, we already found that all IOs are outputs...
  45. logger.debug("internal loop: " + (i_idx >> 1));
  46. writePINs((i_idx | pspecs.getIO_WRITEMask()) & ~(pspecs.getOEPinMask() | pspecs.getCLKPinMask()));
  47. read = readPINs();
  48. out_pins |= (read ^ pspecs.getIO_READMask()) & pspecs.getIO_READMask();
  49. writePINs(i_idx & ~(pspecs.getOEPinMask() | pspecs.getCLKPinMask() | pspecs.getIO_WRITEMask()));
  50. read = readPINs();
  51. out_pins |= ((read ^ ~pspecs.getIO_READMask())) & pspecs.getIO_READMask();
  52. }
  53. pulseClock(idx & ~pspecs.getOEPinMask());
  54. }
  55. logger.info("end... I guessed: 0x" + Integer.toHexString(out_pins) + " / " + Integer.toBinaryString(out_pins)+"b");
  56. return out_pins;
  57. }
  58. private void pulseClock(int addr) {
  59. logger.debug("Pulsing clock with addr: " + Integer.toHexString(addr));
  60. writePINs((addr | pspecs.getCLKPinMask()) & ~pspecs.getOEPinMask()); // Clock high,
  61. writePINs(addr & ~(pspecs.getOEPinMask() | pspecs.getCLKPinMask())); // Clock low
  62. }
  63. private void internal_analisys() {
  64. logger.info("Device: " + pspecs + " Outs: " + Integer.toBinaryString(IOasOUT_Mask)+"b");
  65. int pins, mstate_idx;
  66. writePINs(0x00); // Set the address to 0, enable the /OE pin and leave clock to low
  67. pins = readPINs();
  68. int routstate = pins & pspecs.getROUT_READMask();
  69. logger.info("Registered outputs at start: " + String.format("%02X", routstate));
  70. //logger.info("Output states at start: " + String.format("%02X", (pins & IOasOUT_Mask)));
  71. mstate_idx = routstate >> pspecs.getROUT_READMaskShift();
  72. MacroState ms = new MacroState(buildTag(mstate_idx), mstate_idx, pspecs.getNumROUTPins(), pspecs.getNumINPins());
  73. MacroState nms = null;
  74. mStates[mstate_idx] = ms; // Save it in our Array
  75. logger.info("Added MacroState [" + ms + "] at index " + mstate_idx);
  76. while(true) {
  77. if(ms == null) {
  78. logger.info("There are no more unknown StateLinks we can reach.");
  79. return;
  80. }
  81. nms = analyzeMacroState(ms);
  82. if(nms != null) {
  83. logger.info("We moved to MacroState ["+nms+"]");
  84. ms = nms;
  85. nms = null;
  86. } else {
  87. logger.info("No more StateLinks to generate in ["+ms+"]");
  88. StateLink[] slPath = findPathToNewStateLinks(ms);
  89. if(slPath == null) {
  90. logger.info("Found no paths starting from ["+ms+"]");
  91. ms = null;
  92. } else {
  93. ms = slPath[slPath.length - 1].destSState.macroState; // Mark the new macro state
  94. logger.info("Found a path to another MacroState: ["+ms+"]");
  95. // Traverse the path
  96. for(StateLink sl : slPath) pulseClock(sl.raw_addr);
  97. }
  98. }
  99. }
  100. }
  101. private StateLink[] findPathToNewStateLinks(MacroState start_ms) {
  102. // Search for a state that still has unexplored links
  103. for(int ms_idx = 0; ms_idx < mStates.length; ms_idx++) {
  104. if((mStates[ms_idx] != null) && (mStates[ms_idx] != start_ms)) {
  105. for(int sl_idx = 0; sl_idx < mStates[ms_idx].links.length; sl_idx++) {
  106. if(mStates[ms_idx].links[sl_idx] == null) { // Found an unexplored link, we need to search a path to it
  107. logger.info("Found unexplored link in ["+mStates[ms_idx]+"]");
  108. StateLink[] sll = internal_searchPath(start_ms, mStates[ms_idx]);
  109. if(sll != null) return sll; // Ok, we found a path
  110. }
  111. }
  112. }
  113. }
  114. return null; // Finding nothing
  115. }
  116. private StateLink[] internal_searchPath(MacroState start, MacroState dest) {
  117. // TODO: Implement the search protocol
  118. return null;
  119. }
  120. private MacroState analyzeMacroState(MacroState ms) {
  121. if(!ms.ss_ready) {
  122. logger.info("Generating all possible SubStates for MacroState ["+ms+"]");
  123. genAllMSSubStates(ms);
  124. } else {
  125. logger.info("SubStates already generated for MacroStates ["+ms+"]");
  126. }
  127. int idx_mask = buildInputMask();
  128. int links_counter = 0;
  129. logger.info("Now check if we have a new StateLink to try...");
  130. // Check if we have a link to generate
  131. int maxidx = pspecs.getIO_WRITEMask() | pspecs.getINMask();
  132. for(int idx = 0; idx <= maxidx; idx+=2) {
  133. if((idx & idx_mask) != 0) continue; // Skip this run
  134. if(ms.links[links_counter] == null) {
  135. logger.info("Generating StateLink at index " + links_counter);
  136. pulseClock(idx); // Enter the new state
  137. int pins = readPINs();
  138. int mstate_idx = (pins & pspecs.getROUT_READMask()) >> pspecs.getROUT_READMaskShift();
  139. MacroState nms = mStates[mstate_idx];
  140. SubState ss = null;
  141. StateLink sl = null;
  142. if(nms == null) {
  143. nms = new MacroState(buildTag(mstate_idx), mstate_idx, pspecs.getNumROUTPins(), pspecs.getNumINPins());
  144. mStates[mstate_idx] = nms;
  145. }
  146. ss = generateSubState(nms, idx, idx_mask);
  147. sl = new StateLink(ms.tag, idx, writeAddrToBooleans(idx, idx_mask), ss);
  148. ms.links[links_counter] = sl;
  149. logger.info("Connected MS '"+ms+"' with SS '"+ss+"' ["+nms+"] with SL '"+sl+"'");
  150. return nms;
  151. }
  152. links_counter++; // Keep the counter up to date
  153. }
  154. return null; // We did not move from the macrostate
  155. }
  156. private int buildInputMask() {
  157. return (pspecs.getROUT_WRITEMask() | pspecs.getOEPinMask() | pspecs.getCLKPinMask() | (IOasOUT_Mask << 10));
  158. }
  159. private boolean[] writeAddrToBooleans(int addr, int mask) {
  160. ArrayList<Boolean> instate = new ArrayList<>();
  161. for(int pin_idx = 0; pin_idx < 18; pin_idx++) {
  162. if(((mask >> pin_idx) & 0x01) > 0) continue; // Output pin, not interested
  163. if(((addr >> pin_idx) & 0x01) > 0) instate.add(true);
  164. else instate.add(false);
  165. }
  166. boolean[] barr = new boolean[instate.size()];
  167. for(int idx = 0; idx < barr.length; idx++) barr[idx] = instate.get(idx);
  168. return barr;
  169. }
  170. private SubState generateSubState(MacroState ms, int idx, int idx_mask) {
  171. SubState ss = null;
  172. int pins_1, pins_2, hiz_pins;
  173. ArrayList<Byte> pinstate = new ArrayList<>();
  174. boolean[] instate = null;
  175. writePINs(idx);
  176. pins_1 = readPINs();
  177. // Check that inputs really are inputs
  178. if((pins_1 & pspecs.getIO_READMask() & ~IOasOUT_Mask) != (((idx & pspecs.getIO_WRITEMask()) >> 10) & IOasOUT_Mask)) {
  179. logger.warn("Detected an input that is acting as output when in MS ["+ms+"] -> " + String.format("%02X",
  180. (pins_1 & pspecs.getIO_READMask() & ~IOasOUT_Mask) ^ (((idx & pspecs.getIO_WRITEMask()) >> 10) & IOasOUT_Mask)));
  181. }
  182. writePINs(idx | (IOasOUT_Mask << 10));
  183. pins_2 = readPINs();
  184. // Check that inputs really are inputs
  185. if((pins_2 & pspecs.getIO_READMask() & ~IOasOUT_Mask) != (((idx & pspecs.getIO_WRITEMask()) >> 10) & IOasOUT_Mask)) {
  186. logger.warn("Detected an input that is acting as output when in MS ["+ms+"] -> " + String.format("%02X",
  187. (pins_2 & pspecs.getIO_READMask() & ~IOasOUT_Mask) ^ (((idx & pspecs.getIO_WRITEMask()) >> 10) & IOasOUT_Mask)));
  188. }
  189. hiz_pins = (pins_1 ^ pins_2) & IOasOUT_Mask;
  190. for(int pin_idx = 0; pin_idx < 8; pin_idx++) {
  191. if(((IOasOUT_Mask >> pin_idx) & 0x01) == 0) continue; // Not an output pin we're interested in
  192. if(((hiz_pins >> pin_idx) & 0x01) > 0) pinstate.add((byte)-1);
  193. else if (((pins_1 >> pin_idx) & 0x01) > 0) pinstate.add((byte)1);
  194. else pinstate.add((byte)0);
  195. }
  196. instate = writeAddrToBooleans(idx, idx_mask);
  197. logger.debug("pinstate len: " + pinstate.size() + " instate len: " + instate.length);
  198. Byte[] out_state = pinstate.toArray(new Byte[pinstate.size()]);
  199. int ss_idx = SubState.calculateSubStateIndex(instate);
  200. int ss_key = SubState.calculateSubStateKey(out_state);
  201. logger.debug("SubState index: " + ss_idx + " key: " + ss_key);
  202. ss = ms.ssMap.get(Integer.valueOf(ss_key));
  203. if(ss == null) {
  204. ss = new SubState(ms.tag, ms, out_state);
  205. ms.ssMap.put(Integer.valueOf(ss_key), ss);
  206. } else {
  207. logger.debug("SubState index: " + ss_idx + " key: " +ss_key+ " was already present.");
  208. }
  209. ms.substates[ss_idx] = ss;
  210. return ss;
  211. }
  212. private void genAllMSSubStates(MacroState ms) {
  213. int idx_mask = buildInputMask();
  214. logger.debug("Input mask " + Integer.toBinaryString(idx_mask) + "b");
  215. int maxidx = pspecs.getIO_WRITEMask() | pspecs.getINMask();
  216. for(int idx = 0; idx <= maxidx; idx+=2) {
  217. if((idx & idx_mask) != 0) continue; // Skip this run
  218. logger.debug("Testing combination 0x" + Integer.toHexString(idx));
  219. generateSubState(ms, idx, idx_mask);
  220. }
  221. ms.ss_ready = true;
  222. logger.debug("MacroState ["+ms+"] now has "+ms.ssMap.size()+" SubStates in array of size " + ms.substates.length);
  223. writePINs(0);
  224. }
  225. private int readPINs() {
  226. dpm.writeCommand(DuPALProto.buildREADCommand());
  227. return DuPALProto.handleREADResponse(dpm.readResponse());
  228. }
  229. private int writePINs(int addr) {
  230. dpm.writeCommand(DuPALProto.buildWRITECommand(addr));
  231. return DuPALProto.handleWRITEResponse(dpm.readResponse());
  232. }
  233. static private String buildTag(int idx) {
  234. return String.format("%02X", idx);
  235. }
  236. }