simpctr.html 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <html>
  2. <head>
  3. <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  4. <title>Простой счётчик кода Грея</title>
  5. </head>
  6. <body bgcolor="FFFFFF">
  7. <h1>Простой счётчик кода Грея</h1>
  8. <p>Часто нам нужны компоненты, которые не являются исключительно комбинационными по своей сути - то есть, мы хотим, чтобы компонент имел некоторую память. Существует важная тонкость в объявлении таких компонентов: у вас не может быть компонента, который сам по себе хранит состояние, так как конкретный компонент может встречаться несколько раз в той же схеме. Он не может появиться в схеме несколько раз непосредственно, но он может появиться несколько раз, если он встречается в подсхеме, которая используется несколько раз.</p>
  9. <p>Решение состоит в том, чтобы создать новый класс для представления текущего состояния объекта, и ассоциировать его экземпляры с компонентом через состояние родительской схемы. В этом примере, который реализует реагирующий на фронт 4-битный счётчик кода Грея, мы объявляем класс <code>CounterData</code> для представления состояния счётчика, в дополнение к подклассу <code>InstanceFactory</code>, как было показано раньше. Объект <code>CounterData</code> запоминает как состояние текущего состояния счётчика, так и последнее состояние тактового входа (чтобы обнаруживать передние фронты).</p>
  10. <h2>CounterData</h2>
  11. <pre>
  12. package com.cburch.gray;
  13. import com.cburch.logisim.data.BitWidth;
  14. import com.cburch.logisim.data.Value;
  15. import com.cburch.logisim.instance.InstanceData;
  16. import com.cburch.logisim.instance.InstanceState;
  17. /** Представляет состояние счетчика. */
  18. class CounterData implements InstanceData, Cloneable {
  19. /** Получает состояние, связанное с этим счётчиком в состоянии схемы,
  20. * генерируя состояние, если необходимо.
  21. */
  22. public static CounterData get(InstanceState state, BitWidth width) {
  23. CounterData ret = (CounterData) state.getData();
  24. if(ret == null) {
  25. // Если оно ещё не существует, то мы установим его на наши
  26. // значения по умолчанию, и добавим его в состояние схемы, так что оно может быть получено
  27. // в следующих просчётах.
  28. ret = new CounterData(null, Value.createKnown(width, 0));
  29. state.setData(ret);
  30. } else if(!ret.value.getBitWidth().equals(width)) {
  31. ret.value = ret.value.extendWidth(width.getWidth(), Value.FALSE);
  32. }
  33. return ret;
  34. }
  35. /** Последнее наблюдаемое значение на тактовом входе. */
  36. private Value lastClock;
  37. /** Текущее значение, выдаваемое счётчиком. */
  38. private Value value;
  39. /** Создает состояние с заданными значениями. */
  40. public CounterData(Value lastClock, Value value) {
  41. this.lastClock = lastClock;
  42. this.value = value;
  43. }
  44. /** Возвращает копию этого объекта. */
  45. public Object clone() {
  46. // Мы можем просто использовать то, что возвращает super.clone(): только переменные экземпляра являются
  47. // объектами Value, которые неизменяемы, так что нас не волнует, что и копия,
  48. // и оригинал ссылаются на одни и те же объекты Value. Если бы мы имели изменяемые переменные экземпляра,
  49. // то, конечно, нам пришлось бы клонировать их.
  50. try { return super.clone(); }
  51. catch(CloneNotSupportedException e) { return null; }
  52. }
  53. /** Обновляет последнее наблюдаемое значение на тактовом входе, возвращая истину, если он сработал. */
  54. public boolean updateClock(Value value) {
  55. Value old = lastClock;
  56. lastClock = value;
  57. return old == Value.FALSE &amp;&amp; value == Value.TRUE;
  58. }
  59. /** Возвращает текущее значение, выдаваемое счётчиком. */
  60. public Value getValue() {
  61. return value;
  62. }
  63. /** Обновляет текущее значение, выдаваемое счётчиком. */
  64. public void setValue(Value value) {
  65. this.value = value;
  66. }
  67. }
  68. </pre>
  69. <h2>SimpleCounter</h2>
  70. <pre>
  71. package com.cburch.gray;
  72. import com.cburch.logisim.data.BitWidth;
  73. import com.cburch.logisim.data.Bounds;
  74. import com.cburch.logisim.data.Direction;
  75. import com.cburch.logisim.instance.InstanceFactory;
  76. import com.cburch.logisim.instance.InstancePainter;
  77. import com.cburch.logisim.instance.InstanceState;
  78. import com.cburch.logisim.instance.Port;
  79. import com.cburch.logisim.util.GraphicsUtil;
  80. import com.cburch.logisim.util.StringUtil;
  81. /** Создаёт простой счётчик, который пробегает по очереди 4-битные коды Грея. Этот
  82. * пример показывает, как компонент может сохранять своё внутреннее состояние. Весь
  83. * код, относящийся к состоянию, появляется в классе CounterData. */
  84. class SimpleGrayCounter extends InstanceFactory {
  85. private static final BitWidth BIT_WIDTH = BitWidth.create(4);
  86. // Опять же, заметьте, что у нас нет никаких переменных экземпляра, связанных с
  87. // состоянием конкретного экземпляра. Мы не можем поместить их здесь, потому что только один
  88. // объект SimpleGrayCounter вообще создаётся, и его работа - управлять всеми
  89. // экземплярами, появляющимися во всех схемах.
  90. public SimpleGrayCounter() {
  91. super("Gray Counter (Simple)");
  92. setOffsetBounds(Bounds.create(-30, -15, 30, 30));
  93. setPorts(new Port[] {
  94. new Port(-30, 0, Port.INPUT, 1),
  95. new Port( 0, 0, Port.OUTPUT, BIT_WIDTH.getWidth()),
  96. });
  97. }
  98. public void propagate(InstanceState state) {
  99. // Здесь я получаю состояние, связанное с этим компонентом через вспомогательный
  100. // метод. В данном случае состояние - это объект CounterData, который также есть
  101. // там, где объявлен вспомогательный метод. Этот вспомогательный метод закончит
  102. // созданием объекта CounterData, если ещё ни одного не существует.
  103. CounterData cur = CounterData.get(state, BIT_WIDTH);
  104. boolean trigger = cur.updateClock(state.getPort(0));
  105. if(trigger) cur.setValue(GrayIncrementer.nextGray(cur.getValue()));
  106. state.setPort(1, cur.getValue(), 9);
  107. // (Вы, возможно, подумываете о том, чтобы определить текущее состояние счётчика
  108. // через state.getPort(1). Это ошибка, потому что другой
  109. // компонент может записывать значение в эту же точку, и это
  110. // "испортит" значение, содержащееся там. Нам действительно нужно хранить
  111. // текущее значение в экземпляре.)
  112. }
  113. public void paintInstance(InstancePainter painter) {
  114. painter.drawBounds();
  115. painter.drawClock(0, Direction.EAST); // draw a triangle on port 0
  116. painter.drawPort(1); // draw port 1 as just a dot
  117. // Отобразить текущее значение счётчика по центру прямоугольника.
  118. // Впрочем, если по контексту показывать состояние не надо (при составлении
  119. // вида для печати), то пропустить это.
  120. if(painter.getShowState()) {
  121. CounterData state = CounterData.get(painter, BIT_WIDTH);
  122. Bounds bds = painter.getBounds();
  123. GraphicsUtil.drawCenteredText(painter.getGraphics(),
  124. StringUtil.toHexString(BIT_WIDTH.getWidth(), state.getValue().toIntValue()),
  125. bds.getX() + bds.getWidth() / 2,
  126. bds.getY() + bds.getHeight() / 2);
  127. }
  128. }
  129. }
  130. </pre>
  131. <p><strong>Next:</strong> <a href="counter.html">Gray Code Counter</a>.</p>
  132. </body>
  133. </html>