В предыдущей статье мы узнали какого это компилировать simulavr. Попробовали работать с ним в пошаговом режиме через gdb.
Вызывали прерывания, вручную меняли регистры, вызывали прерывания. Теперь давайте протестируем всё в автоматическом режиме. Вот и задача подвернулась есть датчик DHT11 он работает на однопроводном интерфейсе задача симулировать работу с ним.
Также немного поработаем с декодировщиком sigrok в offline режиме(т.е. без самого логического анализатора).
Table of Contents
Собираем наш simulavr
Здесь все просто, на самом деле нам нужен только verilog, а это потребует установки iverilog и gtkwave, остальные примеры с python и tcl, напишу как до них руки дойдут.
sudo apt-get build-dep simulavr git clone git://git.savannah.nongnu.org/simulavr.git cd simulavr ./bootstrap ./configure --enable-python --enable-verilog --enable-tcl --prefix=/usr make sudo checkinstall
Вообще нужно будет опакетить simulavr по полной для поддержки tcl например понадобился itcl3 itcl3-dev
configure выдаст что то вроде
Summary: build system = Linux AVR_GCC=avr-gcc PYTHON=/usr/bin/python have sphinx python module? yes have rst2pdf python module? no TCL_WISH=/usr/bin/wish8.6 tcl has package Itcl? yes build verilog modul avr.vpi? yes
Прошу пишите если какие то проблемы у вас появились
Вот например:
make[2]: Entering directory '/home/user/source/simulavr/examples/verilog' avr-gcc -c -Wa,-gstabs -x assembler-with-cpp -o right-unit.o right-unit.s right-unit.s: Assembler messages: right-unit.s:17: Fatal error: redefinition of mcu type `avr2' to `attiny25' Makefile:602: recipe for target 'right-unit.o' failed make[2]: *** [right-unit.o] Error 1 make[2]: Leaving directory '/home/user/source/simulavr/examples/verilog' Makefile:485: recipe for target 'all-recursive' failed make[1]: *** [all-recursive] Error 1 make[1]: Leaving directory '/home/user/source/simulavr/examples' Makefile:507: recipe for target 'all-recursive' failed make: *** [all-recursive] Error 1
Тут за время обновления с Ubuntu 15.04 до 16.04 поменялись директивы в GCC поэтому надо в двух файлах:
- simulavr/examples/verilog/right-unit.s
- simulavr/examples/verilog/singlepincomm.s
Заменить .arch ATTiny25 на .arch AVR2
Пример работы с DHT11
С помощью напильника из двух примеров пишем программу которая принимает на один из выводов данные с датчика dht11,а затем передает эти данные на компьютер по UART
Все необходимые для работы примеры находятся в архиве
Внимание!!
К сожалению необходимые verilog модели и привязки в simulavr не устанавливаются с помощью checkinstall, поэтому вам необходимо будет в Makefile поправить, это путь туда где вы скачали и распаковали simulavr
VERILOG_SRC = /home/user/source/simulavr/src
Программа кстати плохая так как в AVR-ку пихается полноценная функция printf так как мы используем в программе подстановки вида %s. Итого программа получает +4.5k при то что их всего 16k
вот головной файл с раскомментированными printf
#include "main.h" #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <inttypes.h> #include <stdlib.h> #include <string.h> #include "DHT.c" #include "uart.c" char h[7]; char t[7]; FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW); //Функция чтения DHT11 char Read_dht(char *_h, char *_t) { char temp[2]={0,0}; unsigned char i; char dht11_dat[5]; stdout = stderr = stdin = &uart_str; for(i=0;i<7;i++) { _h[i]=0; _t[i]=0; } switch (GetDhtData(dht11_dat)) { case DHT_OK: itoa(dht11_dat[0],temp,10); strcpy(_h,temp); itoa(dht11_dat[1],temp,10); strcat(_h,"."); strcat(_h,temp); itoa(dht11_dat[2],temp,10); strcpy(_t,temp); itoa(dht11_dat[3],temp,10); strcat(_t,"."); strcat(_t,temp); return 1; case DHT_ERROR_START_FAILED_1: printf("Err:startfail1"); break; case DHT_ERROR_START_FAILED_2: printf("Err:startfail2"); break; case DHT_ERROR_READ_TIMEOUT: printf("Err:timeout"); break; case DHT_ERROR_CHECKSUM_FAILURE: printf("Err:checksum"); break; } return 0; } int main (void) { uart_init(); stdout = stderr = stdin = &uart_str; _delay_ms(2000); // Время входа в строй датчика DHT11 после включения for (;;){ if (Read_dht(h,t)) { //UART printf("Hum: %s Temp: %s\n",h,t); _delay_ms(600); } } }
Simulavr
Tracepath — следим за сигналами
Итак давайте рассмотрим что будет происходить если к контролеру ничего не подсоединить. Микроконтроллер будет слать в пустоту 20 мс сигналы и ждать ответа с периодичностью 280 мс.
Ещё немалая проблема это то что ваш микроконтроллер может отличаться от тех что поддерживаются simulavr , а ещё он может отличаться от тех микроконтроллеров которые поддерживаются в verilog симуляции, а их совсем мало. Посмотреть можно в папке simulavr/src/verilog вот все богатство моделей поддерживающих verilog:
- avr_ATmega32.v
- avr_ATmega8.v
- avr_ATtiny2313.v
- avr_ATtiny25.v
Сразу скажу, как добавлять новые модели не знаю.
Перед тем как мы что нибудь будем трассировать давайте посмотрим, а какие значения можно смотреть для этого введем команду
simulavr -d atmega32 -o avr.trace
в появившемся файле avr.trace находится список всех параметров, удалите дублирующиеся и ненужные вам параметры.
Теперь запустим симуляцию, на конечное время.
simulavr -c vcd:avr.tracet:trace.vcd -f simul.elf -d atmega16 -F 8000000 -m 6000000
Здесь частота указана в герцах, а время в микросекундах. Частоту указывать обязательно! Теперь можно открыть файл trace.vcd в gtkwave и посмотреть на периодичные пики.
Дописываем в конец Makefile
VCD вывод simulavr на данный момент, не поддерживается Sigrok поэтому пересохраняйте ваш vcd через GTKwave
Ради интереса можете попробовать наш пример с передачей буквы A также, симулировать.
На снимке мы видим то что должно получится, а именно наш вывод. 2 секунды, пока просыпается контроллер, находится в неопределенном состоянии, что можно объяснить строкой
_delay_ms(2000); // Время входа в строй датчика DHT11 после включения
возможно строчка будет закомментирована так как при симуляции 2-секунды превращаются в 30 минут полноценной симуляции.
Затем он начинает посылать с периодичностью ~280 мс, сигналы длинной 20 мс
_delay_ms(250); //Файл DHT.c _delay_ms(20); //Файл DHT.c
Дальше не получив ответа, происходит выход из функции и сообщение об ошибке по UART.
Verilog
Как вы понимаете, на linux есть один GNU-тый verilog — iverilog или icarus verilog
Учим Verilog
На самом деле быстро здесь не получится. Вкратце Verilog это язык описания и работы цифровых устройств. На нем описывают микроконтроллеры и процессоры, затем на заводе преобразуют с помощью библиотек физических элементов, код Verilog в транзисторы и их соединения на кремнии.
Вот пример софта по разводке кремния и вообще куча ссылок вам от увлекающихся людей:
- https://ru.wikipedia.org/wiki/Electric_(%D0%A1%D0%90%D0%9F%D0%A0)
- https://www.linux.org.ru/forum/science/12493405 — что такое Open Hardware?
- http://opencircuitdesign.com/qflow/ -подробней о процессе превращение Verilog кода в железо
это мы отвлеклись в файлах .v располагаются модули это черные ящики наружу которых торчат ножки входов и выводов внутри модулей могут быть ещё модули и все это соединяется вместе. В конечном итоге на верху этой иерархии находится файл тестирования, тоже написанный на верилоге, вот так его например могут обозначить _tb.v
И вот тут мы и подключаемся, наша задача проста написать файл тестирования нашего микроконтроллера, допустим нажимание кнопочек и реакция на это микроконтроллера.
- https://marsohod.org/11-blog/113-icarus — Основная информация на русском языке с базовыми примерами сосредоточена в этом блоге.
- http://electronix.ru/forum/index.php?act=attach&type=post&id=46902 — учебник от того же автора. Спасибо!
- http://allhdl.ru/verilog.php — краткая справка по синтаксису и языку verilog на русском языке.
- http://allhdl.ru/pdf/ieee_std_1364_2001.pdf -исходный английский текст стандарта языка.
Собираем тестовый пример для проверки работы simulavr-verilog
Все необходимые файлы для этого теста находятся в dht_to_uart/buttons_example
Протестируем работу verilog привязки к simulavr. Для этого создадим простой проект. У нас есть atmega32 к PIND которой подключены идеальные кнопки(без дребезга), порт D работает на прием, в это же время PORTB работает на выход и показывает нам инвертированное значение с PIND
#include <avr/io.h> int main (void) { DDRD = 0x00; //порт D - вход //PORTD = 0xFF; //подключаем нагрузочный резистор pullup DDRB = 0xFF; //порт B - выход PORTB = 0x00; //устанавливаем 0 на выходе while(1) { PORTB = ~PIND; //~ знак поразрядного инвертирования } }
Собираем наш elf файл командами
PROGRAM = example MCU = atmega32 MCU_SIM = atmega32 MCU_VER = atmega32 CC = avr-gcc OBJCOPY = avr-objcopy CFLAGS += -Wall -g -Os -mmcu=$(MCU) LDFLAGS += OBJS = $(PROGRAM).o SIMULVERILOG=/home/user/source/simulavr/src/verilog avr-gcc -Wall -g -Os -mmcu=atmega32 -o example.elf example.c
Анализируем verilog тест
На основе примеров из файлов simulavr/example/verilog пишем файл тестирования. Надеюсь что Вы прочли вводную в верилог по ссылкам в предыдущем параграфе.
/* SPI slave Verilog example code. */ `timescale 1ns / 1ns module test; reg clk; reg [7:0] buttons; wire [7:0] pa; wire [7:0] pb; wire [7:0] pc; wire [7:0] pd; assign pd=buttons; defparam avr.progfile="example.elf"; ATmega32 avr(clk, pa, pb, pc, pd); //avr_clock clock(clk); initial begin $avr_trace("avr.trace"); $dumpfile("example.vcd"); $dumpvars(0, test); buttons = 8'b00000000; #30000; #4000 begin buttons=8'b01010101; end // UNMATCHED !! #4000 begin buttons=8'b10101010; end #4000 begin buttons=8'b01010101; end // UNMATCHED !! #4000 begin buttons=8'b10101010; end end initial begin # 100000 $finish; end // initial begin always begin #500 clk<=0; #500 clk<=1; end endmodule
Разберем чуть подробней.
/* SPI slave Verilog example code. */ `timescale 1ns / 1ns module test; reg clk; reg [7:0] buttons; wire [7:0] pa; wire [7:0] pb; wire [7:0] pc; wire [7:0] pd; assign pd=buttons;
timescale — это указание размерности всех единиц находящихся в данном примере. Т.е. цифра вида #500 на самом деле 500 нс.
Далее задаются некие переменные. Это регистр(reg) тактовой частоты clk а также кнопки которые тоже представлены здесь в виде регистра, т.е. того что может хранить значения.
Wire это проводники соединители, они значения не хранят а лишь соединяют между собой в данном случае они нужны будут чтобы подключится к портам микроконтроллера. Да в нашем случае это не просто проводники это шины по 8 проводов в пучке.
assign это объявление о соединения, в данном случае мы соединили регистр кнопок и порт B через проводники pd
defparam avr.progfile="example.elf"; ATmega32 avr(clk, pa, pb, pc, pd);
Здесь мы через параметр указывающий программный файл. А также загруженную модель, инициируем наш микроконтроллер.
Смотрим что получилось
Figure 3: Здесь два теста с pullup и без pullup
И видим непонятные красные полосы — это неопределенное значение xx, а zz — это высокоомное состояние, или желтая полоса по центру.
initial begin $avr_trace("avr.trace"); $dumpfile("example.vcd"); $dumpvars(0, test); buttons = 8'b00000000; #30000; #4000 begin buttons=8'b01010101; end // UNMATCHED !! #4000 begin buttons=8'b10101010; end #4000 begin buttons=8'b01010101; end // UNMATCHED !! #4000 begin buttons=8'b10101010; end end
Здесь всё просто avr_trace — это необязательная функция в которой будут отслеживаться все команды выполняемые процессором
example.elf 0x006c: main OUT 0x11, R1 example.elf 0x006e: main+0x1 LDI R24, 0xff example.elf 0x0070: main+0x2 OUT 0x12, R24 example.elf 0x0072: main+0x3 OUT 0x17, R24 example.elf 0x0074: main+0x4 OUT 0x18, R1 example.elf 0x0076: main+0x5 IN R24, 0x10 example.elf 0x0078: main+0x6 COM R24 SREG=[---S-N-C] example.elf 0x007a: main+0x7 OUT 0x18, R24 example.elf 0x007c: main+0x8 RJMP 76 example.elf 0x007c: main+0x8 CPU-waitstate example.elf 0x0076: main+0x5 IN R24, 0x10
вплоть до остановки нашей симуляции.
dumpfile — это собственно файл куда будут скидываться значения verilog переменных.
dumpvars(0, test); test -это верхний модуль он так и называется в файле example.v , а цифра 0 — указывает на то что нужно, записывать не только переменные верхнего модуля test но и всех переменных подмодулей использованных в головном модуле. 1 — переменные только в топ модуле.
Далее задаем начальное значение для регистра кнопок(buttons) — равное 0 для наглядности пользуюсь бинарной записью.
Ждем 30 мкс или 30 000 нс , затем в течении 4 мкс включаем состояние x55, переключаем xAA опять 4 мкс, повторяем x55 и переключаемся в конце на xAA — это состояние в регистре и останется.
initial begin # 100000 $finish; end // initial begin always begin #500 clk<=0; #500 clk<=1; end
Первая конструкция ждет с начала теста и завершает его по истечении 100 мкс
Вторая конструкция запускает цикл для нашего тактового генератора. в течении 0,5 мкс сигнал равен 1 , в течении оставшихся 0,5 мкс — 0. И это повторяется постоянно пока не закончится тест. Период равен 1 мкс а значит контроллер работает с частотой 1МГц. Кстати если приглядеться то первые 0,5 мкс регистр clk имеет неопределенное состояние, что говорит о том что присваивание происходит в конце временного периода а не в начале.
На примере картинки выше мы видим что контроллер не сразу инициализируется, а также видим задержки в 4-6 тактов при изменении значений в регистре pd что можно перепроверить посмотрев дизассемблер нашего кода и оценив за сколько тактов выполняется один цикл.
Используем Verilog модель датчика DHT11
Все необходимые файлы находятся в папке dht_to_uart/dht11_example
Cпециально для этого занятия была написана verilog модель DHT11.
https://github.com/spacemonkeydelivers/dht11 за что спасибо SMD
Она находиться в файле проекта dht11_to_uart/dht11.v исправленная и откомментированная мной.
Немного справки по двунаправленному выводу — inout . Так как датчик использует один провод для приема и передачи сигнала.
Inout — буфферный элемент, использующийся для двунаправленных сигналов. Этот элемент пропускает через себя входной сигнал, только если на входе CTRL есть управляющая единица. Если на входе CTRL ноль, то элемент отключается от выходного провода переходя в высокоомное состояние. Такие элементы вообще-то используются только для выводов цифровых микросхем. Иными словами, использовать двунаправленные сигналы ( inout ) правильнее всего только в модуле самого верхнего уровня.
Для тестирования нашего модуля с двунаправленным пином. Есть тестирующий файл — dht11_tb.v его и dht11.v -я максимально возможно прокомментировал.
Для быстрого понимания представим все это картинкой
Наш черный ящик DHT11 с одним единственным двунаправленным выводом мы соединяем с двунаправленным выводом data который в случае если out=1 выдает нам значение регистра data_reg. В случае если out=0, провод отрубается от регистра и переходит в высокоомное состояние.
Но так как у нас data соединен с выводом DHT11, то если из DHT11 выходит сигнал то линия будет подтянута(pullup) до его значения.
Как посмотреть вывод датчика
iverilog dht11_tb.v vvp a.out
На выходе будет привычный vcd файл.
Немного магии Sigrok
Некоторые долго смотрят на матрицу и не видят символы, а видят брюнетку, блондинку… ну в общем если вы не можете декодировать и читать протоколы на лету в gtkwave, а там встроенного декодировщика нет. Поэтому давайте вырежем всю нужную нам информацию из файла трасировки(vcd) с помощью Cut/Paste,сохраним(Экспорт -> VCD). Потом немного поколдуем и откроем в Sigrok.
Собираем свежий Sigrok
Увы придется поставить свежий, там есть декодеры DHT11
Совсем свежий 0.4.0 PulseView сейчас крашится при чтение VCD ,ставте 0.3.0
http://sigrok.org/wiki/Building
Последовательность установки а также необходимые пакеты написаны на официальном сайте http://sigrok.org/wiki/Linux опишу лишь проблемы которые у меня возникали.
libsigrok
Попросил python-gi-dev
sigrok-cli
Здесь аккуратней с checkinstall он мне имя пакета как sigrok записал
pulseview
У меня возникала ошибка
pulseview: error while loading shared libraries: libsigrokcxx.so.3: cannot open shared object file: No such file or directory
Все потому что libsigrok в пакетах назывался libsigrok2 а так как я не удалил sigrok и ставил пакеты поверх то линк шел на libsigrok2
Grand Final
А теперь мы зайдем в наш пример dht_to_uart и запустим
make verisim
симулироваться будет долго и это удручающий минус.
Никакой магии, просто выполняются команды по компиляции и исполнению verilog кода
iverilog -o test.vvp dht11_atmega32_tb.v $(VERILOG_SRC)/verilog/avr_ATmega32.v $(VERILOG_SRC)/verilog/avr.v vvp -M $(VERILOG_SRC) -mavr test.vvp
После чего Вам останется пересохранить файл example.vcd в Gtkwave, нам нужны проводники uart_tx и dht_data , заодно проверьте все ли в порядке. Попытка вырезать отдельный пин из шины и экспортировать его увы не удалась ,возможно я просто плохо знаю Gtkwave.
Откройте файл vcd в Pulseview укажите количество каналов как 2 а параметр Downsample как 10000, иначе PulseView съест всю вашу память.
Примените декодеры DHT к нужной линии и декодер UART(bd 9600) к другой
Выводы, мы можем автоматизированно создавать тесты и проверять работу, каждый раз не собирая стенд. За это мы платим скоростью работы.
Если вы смогли повторить этот эксперимент из желудей и палок, это круто.
Все же большим достижением из этой работы была бы налаженная коммуникация между GTKwave и Sigrok это поможет всем. Ну хотя бы что VCD чуть по умней читалось а не съедало всю память.
Добавить комментарий