201310121002


Язык схемотехники функциональных блоков (Siemens Logo! FBD) или язык программирования (С)?

    Godzilla vs. SpaceGodzilla (ゴジラVSスペースゴジラ)
    перевод: Годзилла и пришелец из космоса
    
Схемотехники безусловно предпочтут осуществить переход от создания электронных схем в программирование микропроцессоров посредством языка схем (электротехника, электроника, цифровая техника). Столь же очевидно, что программисты предпочтут для перехода в мир программирования контроллеров язык программирования. Писать транслятор с языка схем в машинный код будут программисты. Большие микросхемы VLSIC (very large scale integrated circuit), FPGA (field-programmable gate array), CPLD (complex programmable logic devices), главным образом, создаются и/или настраиваются под конкретную задачу с помощью языков программирования (ABEL, Verilog, VHDL и др.). Кроме того, по традиции, описание электронных схем для использования в симуляторах и программах расчета схем на компьютере, делается с помощью так называемых "входных" языков, родственных языкам программирования (SPICE).

Наборов команд (сильно отличающихся между собой) программирующих чип или программу-транслятор насчитывается сотни или тысячи. Объединяющими языками (пишем на одном языке, результат зависит от цели: x86+Windows, x64+OSX, ARM+Android 4.4, AVR, Microchip PIC..) "де-факто" может служить, например, C (может пригодиться во многих других областях) и SPICE (Simulation Program with Integrated Circuit Emphasis, используется с 1973 по сей день, основан на разработках 1960 года).

Таковы некоторые "за" и "против" программирования контроллеров с помощью языка C.

В этой работе я попытаюсь смягчить переход от простых логических схем к простым текстам на C, для тех кто знаком с языком схем. По стилю, использование обучающих проектов (vmlab 3.15 + winavr C 20100110) близко к стилю проектов "opensource" -- от того, что видны сложность и устройство модулей, смысл которых пока не ясен, мы не перестаем ими пользоваться. Так в случае проектов, подобных GNU, нет ни одного скрываемого фрагмента -- не доверяете какому то модулю -- проверьте. Нет времени проверить -- дайте удостовериться в надежности кода доверенным лицам. Обнаружили ошибку, уязвимость, недостающий или лишний фрагмент кода -- опубликуйте, и изменения будут внесены и доступны для всех желающих. 

При обучении всегда работает принцип: целое разобъем на две части

  1. Сложная, непонятная -- использую, доверяя тем, кто способен убедиться в ее полезности.
  2. Понятная -- сам написал. Поскольку первую часть можно читать всем желающим, возникает желание разобраться в более сложных вещах, чтобы применить изученное в своей части проекта. Так начальное состояние: "1 -> весь проект, 2 -> пусто" со временем переходит к наполнению второй части, не забывая о том, что для самостоятельного создания большого проекта в одиночку, скажем мобильного смартофона "open hardware" на операционной системе c открытым исходным кодом "из песка" (Silica si2o, [1][2]), потребуются миллионы лет.

Коды коммерческого проекта "Microsoft Windows" также открыты для пользователей,.. способных проанализировать гигабайты исходных текстов и.. купить их. Кроме того фирма должна убедиться, что покупатель заслуживает доверия и способен гарантировать отсутствие утечки приобретаемой информации.

Сложную часть поместим в подобие библиотеки -- в простейшем случае пользуемся ей методом "copy-paste".

Теперь займемся созданием схем с возможностью использования микроконтроллеров.

           +--------+
           |    UDF |
  5V o-----+I2      |
           |      Q1+------
           +I1      |
           |      Q2o------
     +-----+I0      |
     |     |        |
     |     +--------+
    ---
    
Пусть имеется блок UDF (user defined function) , с тремя входами (обычно слева) и двумя выходами (обычно справа). Вход I2 не подключен (в различных случаях это приводит к разным последствиям -- напряжение на нем подтянется к питающему (VDD), к нулевому (GND, VSS), либо начнет произвольно изменяться (fluctuate, часто это плохо). Кроме того, на схемах не показаны случаи отказа элемента. В математике вместо слова блок удобнее сказать функция. Три входа -- функция трех переменных, один выход -- скалярная функция, несколько -- векторная. В языке C можно полностью заимствовать математический подход (а),

       +----+                     
  d ---+  fa|   w  +----+
       |    +------+  fc|
  c ---+    |      |    |
       +----+      |    +--- y    y = fc( fa( c, d), fb( a, b))      (а)
                v  |    |
       +----+   +--+    |         fa_error = fa( OUT w, IN c, IN d)
  b ---+  fb|   |  +----+         fb_error = fb( OUT v, IN a, IN b)  (б)
       |    +---                  fc_error = fc( OUT y, IN v, IN w)
  a ---+    |
       +----+   

а можно зарезервировать "результирующее значение функции для сообщений о внештатных ситуациях (б). Подход Б удобно использовать в случае, когда выходов больше чем один, например:

  f_err = f( OUT x, OUT y, OUT z, IN a, IN b, IN c, IN d)            (в)
  
Следует помнить, что компилятор языка C ничего не знает о том, что такое OUT и IN, поэтому их следует определить до вызова функции. Понимание устройства блока (тела функции) потребует несколько более близкого знакомства с языком C, на первых порах можно пользоваться готовыми библиотеками функций (логических ячеек).

Пример

Двухвходовый аналоговый сумматор

  // compile with gcc asum.c -o asum

  #include <stdio.h>

  #define IN
  #define OUT &

  int asum( int* z, int x, int y) {
    *z = x + y;
    return 0;
  }

  int main() {
    int x = 5, y = 4, z = 0;
    int err = asum( OUT z, IN x, IN y);
    
    // debug
    printf("error %d, output %d\n", err, z);
    return 0;
  }


Неподключенные входы
--------------------

Таким аргументам назначаются значения по умолчанию. Пример приводится на С++ в стиле C (1983). Ранние версии языка (С, 1971) этого не допускают.

Пример

  // compile with g++ asum.cpp -o asum

  #include <stdio.h>

  #define IN
  #define OUT &

  int asum( int* z, int x, int y = 1) {
    *z = x + y;
    return 0;
  }

  int main() {
    int x = 5, y = 4, z = 0;
    int err = asum( OUT z, IN x);
    
    printf("error %d, output %d\n", err, z);
    return 0;
  }

Ссылки:

[1]. http://www.nds-1c.ru/product/pesok/1.1.2
[2]. http://en.wikipedia.org/wiki/Silicon_dioxide
[3]. http://en.wikipedia.org/wiki/Class_variable

------------------------------------------------------------------------

Приложение А. Возвращаемое значение функции это выходное значение блока.

Стандартные блоки.
Дигитальная техника.

Замечание. По одному триггеру каждого типа на схему. Ограничение связано с тем, что static работает подобно класс-переменным [3] -- меняешь в одном экземпляре, а читаешь измененное значение в любом другом сконструированном объекте.

#include <stdio.h>
#define U8 unsigned int

U8 AND(U8 ax, U8 ay) {
  return (ax && ay);
}

U8 OR(U8 ax, U8 ay) {
  return (ax || ay);
}

U8 XOR(U8 ax, U8 ay) {
  return ax && ! ay || !ax && ay;
}

U8 NAND(U8 ax, U8 ay) {
  return !(ax && ay);
}

U8 NOR(U8 ax, U8 ay) {
  return !(ax || ay);
}

U8 NOT(U8 ax) {
  return !ax;
}

U8 SR(U8 as, U8 ar) { // 11-behaviour aka Logo! latching relay
  static U8 s = 0, r = 0;
  if ( as && !ar) { s = 1; r = 0; }
  if (!as &&  ar) { s = 0; r = 1; }
  if ( as &&  ar) { s = 0; r = 0; }
  return s;
}

U8 D(U8 ad, U8 ac, U8 ar) {
  static U8 d = 0;
  if ( ac) d = ad ;
  if ( ar) d = 0;
  return d;
}

int main() {
   U8 x, y, d, c, r;
   
   printf("\nx   y   x xor y\n");

   x = 0; y = 0; printf("%d   %d   %d\n", x, y, XOR( x, y));
   x = 0; y = 1; printf("%d   %d   %d\n", x, y, XOR( x, y));
   x = 1; y = 0; printf("%d   %d   %d\n", x, y, XOR( x, y));
   x = 1; y = 1; printf("%d   %d   %d\n", x, y, XOR( x, y));

   printf("\nwriting data bit 1:\n");
   printf("d c r D(d)\n");

   d = 0; c = 0; r = 1; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 1; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 1; c = 1; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 1; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 1; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
                                      
   printf("\nwriting data bit 0:\n");   
   printf("d c r D(d)\n");
   
   d = 0; c = 0; r = 1; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 1; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 1; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));
   d = 0; c = 0; r = 0; printf("%d %d %d %d\n", d, c, r, D( d, c, r));

   return 0;
}
