fpga-systems-magazine

Сага о светодиодах. Часть 3. Ведомая сторона.

Главная » Статьи » Разное » Общее
sergebalakshiy
09.07.2022 22:59
1106
0
5.0

Оглавление

Работа над ошибками

Драйвер светодиодов

Конструкция модуля led_dev

Испытательный стенд для led_dev

Конструкция модуля i2c slave

Тестирование I2C Slave

Использование Quartus

Скрипт mkproject.tcl

Какая автоматизация без Makefile!

Итоги

 

Работа над ошибками

Вернемся к части первой. И поработаем над таким вот замечанием.

"возникли вопросы к листингу кода. Например в листинге 2 (и не только) есть подобный код. Смущает код, описанный после ресета. У нас тут синхронный ресет (хотя при асинхронном я бы тоже так не делал). И два if независимых. В первом по RST_N==0 сигнал shift_cnt <= 0. Во втором при count == time1 и shift_cnt != 2'b10 сигнал shift_cnt <= shift_cnt + 1'b1. А так как второй if не зависит о  первого (перед вторым if нету else) то непонятно что будет если эти условия выполнятся одновременно."

Автор этого замечания Artem Senin.

 

Управление светодиодом как было:


always @(posedge CLK_IN)begin
    if(RST_N==0)begin
        count <= 25'b0;
        rledout <= 3'b1;
        shift_cnt <=2'b0;
    end
    if(count == time1) begin
        count<= 25'd0;
        if(shift_cnt==2'b10)begin
            rledout <= 3'b1;
            shift_cnt <=2'b0;
        end else begin
            rledout <= {rledout[1:0],1'b0};
            shift_cnt <= shift_cnt + 1'b1;
        end
    end else
        count <= count + 1'b1;
    end

 

Quartus для этого модуля генерирует вот такую схему,

Теперь изменим модуль так, чтобы в нём не было независимых утверждений jf.

Управление светодиодом с внесенными исправлениями:


always @(posedge CLK_IN)begin
    if(RST_N==0)begin
        count <= 25'b0;
        rledout <= 3'b1;
        shift_cnt <=2'b0;
    end else begin
        if(count == time1) begin
            count<= 25'd0;
            if(shift_cnt==2'b10)begin
                rledout <= 3'b1;
                shift_cnt <=2'b0;
            end else begin
                rledout <= {rledout[1:0],1'b0};
                shift_cnt <= shift_cnt + 1'b1;
            end
        end else begin
            count <= count + 1'b1;
        end
    end
end

Теперь для каждого условия в модуле есть альтернативная часть. Функциональная схема, в таком случае, приобретает вот такой вид.

Сравним полученные схемы. В схеме собранной по первому модулю есть только два мультиплексора, которые явно управляются сигналом RST. И это мультиплексор shift_cnt и rledout. Сброс счетчика count происходит не явно.

В схеме по исправленному модулю уже есть три мультиплексора, которые переключаются сигналом RST и производят сброс именно так как описано в модуле.

Отсюда правило. Указывать в модуле альтернативные варианты для условий if, чтобы в схемах не возникало неявных цепей.

Впрочем это правило я уже встречал где-то в литературе, посвященной проектированию на verilog.

И еще напомню, что источник выше обсуждаемого кода находится здесь

Драйвер светодиодов

В предыдущей части 2 рассматривалась конструкция правой части уравнения рисунок 1 "Блок схема устройства с блоком I2C". Теперь рассмотрим конструкцию в левой его части. Эта часть состоит из двух устройств:

  • модуль i2c_slave - приемник управляющих сигналов;
  • модуль led_dev - драйвер светодиодов.

Назначение и принцип работы i2c_slave известны из его описания. Назначение драйвера светодиодов состоит в том, чтобы определять по принятым данным какую линию светодиодов необходимо активировать и собственно активировать эту линию. В таблице представлено соответствие переданных управляющих слов и линий светодиодов.

Соответствие входных и выходных последовательностей
Вход Выход
8'h00 8'b00000000
8'h01 8'b00000001
8'h02 8'b00000010
8'h03 8'b00000100
8'h04 8'b00001000
8'h05 8'b00010000
8'h06 8'b00100000
8'h07 8'b01000000
8'h08 8'b10000000
 

Из таблицы понятно, что центральную часть разрабатываемого устройства будет занимать дешифратор последовательности байт, как показано в таблице. Задача несколько усложняется тем, что необходимо как-то взаимодействовать с устройством i2c_slave в плане ожидания от него управляющей последовательности байт.

Конструкция модуля led_dev

Напишем модуль, который будет выполнять работу, как описано выше.

Модуль led_dev


`include "timescale.v"
module led_dev (
        clk,
        rst,
        dataIn,
        leds);
    input clk;
    input rst;
    input  [7:0] dataIn;

    output reg [7:0]  leds;

    always @(posedge clk) begin
        if (rst) begin
        leds <= 8'h00;
        end
        else begin
         case (dataIn)
            8'h00:leds <= 8'h01;
            8'h01:leds <= 8'h02;
            8'h02:leds <= 8'h04;
            8'h03:leds <= 8'h08;
            8'h04:leds <= 8'h10;
            8'h05:leds <= 8'h20;
            8'h06:leds <= 8'h40;
            8'h07:leds <= 8'h80;
            default:leds <= 8'h00;
        endcase // case (dataIn)
      end // else: !if(rst)
   end // always @ (posedge clk)
endmodule

Рисунок этой схемы представлен ниже.

Один байт управляющего слова поступает на вход дешифратора, где происходит распознавание линии светодиодов в соответствии с таблицей 2.1, затем номер линии через мультиплексор подается на выходной регистр и через него на выходные порты, к которым и будут подключены светодиоды. Сигнал RST выключает все светодиоды, подавая через мультиплексор сигнал 8’h00 на выходной регистр, переводя в низкое состояние выходные линии.

Испытательный стенд для led_dev

Хотя работа и схема устройства led_dev довольно просты, все же поставим это устройство на испытательный стенд и убедимся в правильности его работы.

Конструкция испытательного стенда для led_dev представлена в следующем модуле. Комментарии по коду ускорят его понимание.

Стенд для проверки устройства led_dev:


`include "timescale.v"
module led_dev_tb ();

   //Создадим тактовый опорный генератор
   reg clk;
   always #10 clk = ~clk;
   // Поставим триггер rst
   reg rst;
  // Поставим счетчик 3-х битный. Будет задавать 
 // входную последовательность сигналов
   reg [2:0] cnt;
   // Подключим его к регистру на 8 бит
   reg [7:0] regPI;
   // Этот регистр имитирует выходной регистр 
   // устройства i2c, с которым led_dev будет
   // в дальнейшем работать.
   
   // Соберем из счетчика и регистра генератор байт
   // с выходным регистром
   always @( posedge clk) begin
      cnt <= cnt + 1;
      regPI [2:0] <= cnt;
      regPI [7:3] <= 0;
      $display( "CNT:", cnt );
   end 
   // Создадим провода для светодиодов
   wire [7:0] leds;
       
   //Подключим к собранному стенду led_dev
   led_dev iu_led_dev(
       .clk(clk),
       .rst(rst),
       .dataIn(regPI),
       .leds(leds));

   // Начнем процесс
   initial begin
      clk = 0;
      rst = 0;
      // Сделаем первоначальный сброс
      #30 rst = 1;
      #10 rst = 0;
      #20;
      // Считаем с 0
      cnt = 3'b0;
      @(posedge clk)
 #0;
      begin
  // Смотрим что получилось
  $display(leds);
      end
   end

   // И включим все это в работу
   initial begin
      // Зададим количество работы
      #60000 $finish;
      // сформируем файл для gtkwave
      $dumpfile("out.vcd");
      $dumpvars(0,led_dev_tb);
   end
endmodule // led_dev_tb

Из комментариев должно быть понятно, как работает стенд. На рисунке ниже приведены эпюры в некоторых точках устройства, чтобы визуально проконтролировать работу led_dev.

Счетчик cnt, собранный на стенде, задает входную управляющую последовательность сигналов. Далее эти сигналы с выходных линий счетчика подаются на вход регистра regPI. Этот регистр имитирует работу выходного регистра модуля i2c slave. И далее на эпюрах видно как происходит дешифрирование входного сигнала и "засветка" соответствующих светодиодов.

Конструкция модуля i2c slave

Первое с чем я столкнулся в этой работе это то, что модуль i2c_slave из проекта взятого здесь и с которым я успешно работал используя i2c_master в качестве ведущего устройства при сборке в Quartus выдавал множество ошибок. Некоторые ошибки я смог исправить, а некоторые так и не смог, так как они требовали существенной переделки модуля. Вносить изменения в модуль i2c_slave я не стал. Порывшись в интернете я нашел несколько других модулей i2c, проверил их работу и остановился на модуле i2cSlave из проекта на OpenCode. Исходный код этого проекта можно скачать отсюда. Что я и сделал.  Скачал этот проект. В модуле i2cSlaveTop собрал устройство led_dev совместно с модулем i2cSlave, см. следующий листинг.

 

Модуль i2cSlaveTop


//////////////////////////////////////////////////////////////////////
//// ////
//// i2cSlaveTop.v ////
//// ////
//// This file is part of the i2cSlave opencores effort.
////  ////
//// ////
//// Module Description: ////
//// You will need to modify this file to implement your
//// interface.
//// ////
//// To Do: ////
////
//// ////
//// Author(s): ////
//// - Steve Fielding, sfielding@base2designs.com ////
//// ////
//////////////////////////////////////////////////////////////////////
//// ////
//// Copyright (C) 2008 Steve Fielding and OPENCORES.ORG ////
//// ////
//// This source file may be used and distributed without ////
//// restriction provided that this copyright statement is not ////
//// removed from the file and that any derivative work contains ////
//// the original copyright notice and the associated disclaimer. ////
//// ////
//// This source file is free software; you can redistribute it ////
//// and/or modify it under the terms of the GNU Lesser General ////
//// Public License as published by the Free Software Foundation; ////
//// either version 2.1 of the License, or (at your option) any ////
//// later version. ////
//// ////
//// This source is distributed in the hope that it will be ////
//// useful, but WITHOUT ANY WARRANTY; without even the implied ////
//// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR ////
//// PURPOSE. See the GNU Lesser General Public License for more ////
//// details. ////
//// ////
//// You should have received a copy of the GNU Lesser General ////
//// Public License along with this source; if not, download it ////
//// from  ////
//// ////
//////////////////////////////////////////////////////////////////////
//
`include "i2cSlave_define.v"

module i2cSlaveTop (
     clk,
     rst,
     sda,
     scl,
     leds
     );
   input clk;
   input rst;
   inout sda;
   input scl;
   //output
   wire [7:0] myReg0;
   output [7:0] leds;
   i2cSlave u_i2cSlave(
        .clk(clk),
        .rst(rst),
        .sda(sda),
        .scl(scl),
        .myReg0(myReg0),
        .myReg1(),
        .myReg2(),
        .myReg3(),
        .myReg4(8'h12),
        .myReg5(8'h34),
        .myReg6(8'h56),
        .myReg7(8'h78)
        );

   led_dev u_led_dev (
       .clk(clk),
       .rst(rst),
       .dataIn(myReg0),
       .leds(leds)
       );
endmodule

Для контроля создал проект в Quartus. На рисунке представлена полученная функциональная схема модуля i2cSlave совместно с устройством led_dev.

 

Тестирование I2C Slave

Следующая задача, которую необходимо было решить это тестирование собранного устройства. В составе проекта i2cSlave имеется стенд для тестирования самого устройства i2c. Я решил воспользоваться данным стендом. Устройство led_dev, на самом деле, я присоединил к i2cSlase прямо на стенде. Создал в модуле testHarness провода для подключения модуля led_dev и подключил его к модулю i2cSlave как показано в листинге. Весь код я не показываю, только часть в которой выполняется подключение модуля led_dev.

 

Модуль testHarness



// -------------------------- testHarness.v -----------------------
`include "timescale.v"

module testHarness ();
// ...
i2cSlave u_i2cSlave(
  .clk(clk),
  .rst(rst),
  .sda(sda),
  .scl(scl),
  .myReg0(),
  .myReg1(),
  .myReg2(),
  .myReg3(myReg0w),
  .myReg4(8'h12),
  .myReg5(8'h34),
  .myReg6(8'h56),
  .myReg7(8'h78)
);

led_dev u_led_dev (
.clk(clk),
.rst(rst),
.dataIn(myReg0w)
);

// ...
endmodule

Теперь осталось разработать источник управляющих сигналов для передачи в подчинённое устройство. Для этого в файле testCase0.v добавим 32-х битный счетчик cnt и создадим цикл, который будет передавать, в моём случае 4 раза, последовательность байт cnt.

 

Модуль testCase0


// ---------------- testcase0.v ---------------------
`include "timescale.v"
`include "i2cSlave_define.v"
`include "i2cSlaveTB_defines.v"
module testCase0();
reg ack;
reg [7:0] data;
reg [15:0] dataWord;
reg [7:0] dataRead;
reg [7:0] dataWrite;
integer i;
integer j;
integer C;
reg [31:0] cnt = 32'b0;
initial
begin
  $write("\n\n");
  testHarness.reset;
  #1000;
  $write("Testing register read/write\n");
  testHarness.u_wb_master_model.wb_write(1, `PRER_LO_REG , 8'h17);
  testHarness.u_wb_master_model.wb_write(1, `PRER_HI_REG , 8'h00);
  testHarness.u_wb_master_model.wb_cmp(1, `PRER_LO_REG , 8'h17);
  // enable i2c master
  testHarness.u_wb_master_model.wb_write(1, `CTR_REG , 8'h80);
   repeat (4) begin
      for (i=0; i<8; i=i+1) begin
 $display( "Loop:", i, "cnt:", "0x%4h",cnt, " b%32b", cnt );
     multiByteReadWrite.write( \
           {`I2C_ADDRESS, 1'b0}, \
           8'h00, cnt, `SEND_STOP);
     cnt = cnt + 1;
      end
      cnt = 0;
   end

  $write("Finished all tests\n");
  $finish;
end
endmodule

Собирём стенд и посмотрим его в работе.

Из рисунка видно, что устройство i2cSlave правильно принимает управляющие байты. Устройство led_dev принимает эти байты и правильно их дешифрирует, "зажигая" соответствующие светодиоды.

 

Использование Quartus

Я уже несколько раз упоминал, что в своей работе над проектом использую Quartus. Но при этом особо не рассказывал как я это делаю.
Конечно есть мышино-визуальный способ использования этого замечательного инструмента. Но замечательным этот инструмент делает не только то, что он умеет откликаться на мышиную возню и компилировать и строить проекты, но также и то, что он позволяет, где это оправдано, применять автоматизацию с использованием TCL скриптов, а также вездесущей команды make.

И в этом небольшом отступлении я и хочу немного рассказать как я это использую.
Для начала мне приходилось создавать множество разных проектов для тестирования тех или иных вещей. И уходило много времени на создание всех этих каталогов, файлов, тестов и их вариантов. Поэтому я решил создать несколько tcl скриптов для выполнения различных рутинных действий. В частности один из этих скриптов должен будет, как мне хотелось, создавать новый проект, настраивать его для работы.
Чтобы начать пользоваться этой возможностью я погуглил в интернете, нашел несколько ресурсов, на которых раскрывается данная тема. В частности об этом пишут на хабре. Здесь я приведу один из источников в котором описана данная технология. Это Tcl Scripting, Quartus II Handbook version 11.1, Volume 2.

Для начала попробуем создать скрипт, при помощи которого можно создать новый проект. Хотелось, чтобы я мог написать в командной строке что-то вроде такого:

Командная строка для Quartus


$> quartus_sh -t new_project.tcl -project project_name

И проект готов.
Рассмотрим подробнее эту строку. Первым в ней идет указание на запуск quartus без графической оболочки. Далее идет параметр -t за которым указывается имя скрипта tcl. За именем скрипта указываются параметры для самого скрипта.

В выше упомянутом руководстве я нашел вариант того, как можно получить доступ к параметрам скрипта и как можно использовать полученные значения. Для этого применяется следующая конструкция:

Пример скрипта для работы с аргументами


package require cmdline
variable ::argv0 $::quartus(args)
set options {
 { "project.arg" "" "Project name" }
 { "frequency.arg" "" "Frequency" }
 { "argument.arg" "" "Argument Name" }
}
set usage "You need to specify options and values"
array set optshash [::cmdline::getoptions ::argv $options $usage] (*@\label{com:array}@*)
puts "The project name is $optshash(project)"
puts "The frequency is $optshash(frequency)"
puts "The argument is $optshash(argument)"

Для работы этого скрипта понадобится модуль cmdline. Далее мы создаём список параметров: имя_параметра значение_параметра описание_параметра.
В строке 9 из листинга предписывается заполнить массив с определенными выше параметрами их значениями, считанными из командной строки. И далее, просто печатаем имена параметров и их значения. Если параметр со значением не был указан, то значение параметра пусто.

Вывод при запуске этого скрипта будет следующим:

Пример работы в командной строке


$ quartus_sh -t print_cmd_args.tcl -project aaa -argument bbb
...
Info: Quartus(args): -project aaa -argument bbb
The project name is aaa
The frequency is
The argument is bbb
...

Итак, с помощью этих не хитрых строк можно получать из командной строки значения параметров. Теперь напишем скрипт, который будет получать имя проекта и создавать проект с этим именем. Снова обратимся к руководству Quartus. В нем приводится пример скрипта, с помощью которого можно создать проект, открыть и закрыть его. Возьмем этот скрипт за основу. Выглядит он вот так:

Полнофункциональный метод открытия проектов


    package require cmdline
    variable ::argv0 $::quartus(args)
      set options { \
    { "project.arg" "" "Project Name" } \
    { "revision.arg" "" "Revision Name" } \
   }
array set optshash [::cmdline::getoptions ::argv0 $options]
# Ensure the project exists before trying to open it
if {[project_exists $optshash(project)]} {
    if {[string equal "" $optshash(revision)]} {
        # There is no revision name specified, so default
        # to the current revision
        project_open $optshash(project) -current_revision
    } else {
        # There is a revision name specified, so open the
        # project with that revision
        project_open $optshash(project) -revision \
        $optshash(revision)
    }
    } else {
        puts "Project $optshash(project) does not exist"
     exit 1
    }
# The rest of your script goes here

Здесь, если проект существует, и не указана ревизия проекта, то мы открываем проект с текущей ревизией, если ревизия указана, то открываем проект с указанной ревизией, иначе сообщаем о том, что проект не существует.
Для создания проекта нам понадобится добавить в этот скрипт еще несколько строк в той ветке else, в которой обнаружено, что проекта с таким именем не существует. Добавим сюда строки для создания проекта.

Команда для создания проекта


...
project_new $optshash(project) -revision $optshash(project) -overwrite
...

И добавим строки с указанием устройства, для которого создаётся проект и имя семейства этих устройств, а также сколько процессоров можно использовать при построении проекта.

Команды назначения устройства

...

 set_global_assignment -name DEVICE 10M50DAF484C7G
 set_global_assignment -name FAMILY "MAX 10"
 set_global_assignment -name NUM_PARALLEL_PROCESSORS 4

...

Далее, я хотел бы, чтобы у меня была определенная структура каталогов для проектов, я называю это окружением проекта а иногда тестовым окружением в зависимости от того с чем я работаю. Так на самом верху у меня есть каталог определяющий общую тему проекта. Допустим это каталог fpga-system поскольку я занимаюсь написанием статей для этого ресурса. Ниже имеется каталог common, в котором содержатся различные модули на verilog содержащие общие модули и функции для всех проектов в категории fpga-systems. Кроме того, здесь же находятся каталоги с исходными текстами статей, которые я пишу. Далее располагаются каталоги указывающие на семейство устройств, под которые создаются проекты. Поскольку у меня таких устройств два, то и каталогов тоже два, а именно de10_lite для платы 10M50DAF484C7G и Sipeed Tang Premier RISC-V c чипом EG4S20BG256. В каждом из этих каталогов также находится каталог common с общими, но специфичными для данного устройства файлами. Далее идут каталоги с названиями проектов. Это будет верхний уровень проекта, в нем, как правило хранится файл с именем top.v,  или как-то по другому, в котором содержится самый верхний модуль проекта. В каждом таком каталоге создаётся один рабочий каталог, в котором происходит сборка проекта и хранятся различные файлы и каталоги создаваемые при построении проекта. Эта структура является `идеальной` структурой и не является, в следствии своей идеальности рабочей структурой. Но она дает общее представление о том, как я хотел бы вести свои дела, и как, в связи с этим, я хотел бы упорядочивать порождаемые в ходе работы данные с тем, чтобы потом можно было просто воспроизвести все, что происходило во время работы.

И еще раз напомню, что эта структура каталогов не настоящая. Настоящее окружение несколько отличается от этого. И оно встраивается в рабочий процесс по созданию какого-то конкретного продукта. Например, для того, чтобы написать статью я разрабатываю и конкретное устройство и его тестирование и его описание и конечным продуктом является статья на сайте с описанием этого устройства. И таким образом, статья и разработанное устройство находятся в одном окружении. И когда я смотрю на верхний уровень этого окружения, то мне сразу понятно, что здесь находится, и внутри есть полное описание того, что здесь происходило.

Скрипт mkproject.tcl

Располагаем  скрипт в каталоге, в котором необходимо создать проект. И запускаем скрипт указывая имя проекта  и имя модуля верхнего уровня.
Скрипт должен:

  • принять параметры;
  • создать пару директорий, одну для документации, другую для работы;
  • создать проект;
  • выполнить некоторые назначения по проекту.

Реализация этих действий показана в листинге

Скрипт mkproject.tcl


package require ::quartus::project
package require ::quartus::flow
package require cmdline

variable ::argv0 $::quartus(args)
set options { \
              { "project.arg" "" "Project Name" } \
              { "revision.arg" "" "Revision Name" } \
              { "module_top.arg"  "" "Module top file" } \
            }
set usage "You need to specify options and values"
array set optshash [::cmdline::getoptions ::argv $options $usage]
puts "The project name is $optshash(project)"
set need_to_close_project 0
set make_assignments 1

# Set up the appropriate directory structure
# and move the files to their places
set partitions "run doc"
set run 0
# make directories $partitions
foreach partition $partitions {
    if { [file exists $partition] == 0 } {
        file mkdir $partition
        puts "file $partition was created"
    }
}
# make project
if { ! [project_exists
        [lindex $partitions $run]/$optshash(project)]} {
    project_new \
        [lindex $partitions $run]/$optshash(project) \
        -revision $optshash(project) \
        -family "MAX 10" \
        -part 10M50DAF484C7G

    puts "[lindex $partitions $run]/$optshash(project) was created"
} else {
    puts "$optshash(project) is exists"
}

# Set the device, the name of the top-level BDF,
# and the name of the top level entity
if {$make_assignments} {
    # Ensure the project exists before trying to open it
    if {[project_exists \
            [lindex $partitions $run]/$optshash(project)]} {
        if {[string equal "" $optshash(revision)]} {
            # There is no revision name specified, so default
            # to the current revision
            project_open [lindex \
                    $partitions $run]/$optshash(project) \
                -current_revision
        } else {
            # There is a revision name specified, so open the
            # project with that revision
            project_open [lindex \
                    $partitions $run]/$optshash(project) \
                -revision \
                $optshash(revision)
        }
        puts "Project $optshash(project) is open!"
    }
    set fam "MAX 10"
    set mtop $optshash(module_top)
    set_global_assignment -name FAMILY $fam
    set_global_assignment -name VERILOG_FILE ../$mtop.v
    set_global_assignment -name TOP_LEVEL_ENTITY $mtop
    set_global_assignment -name SDC_FILE $mtop.sdc

    set_global_assignment -name NUM_PARALLEL_PROCESSORS 4
    set_global_assignment -name PROJECT_OUTPUT_DIRECTORY .
    set_global_assignment -name SEARCH_PATH ..
    set_global_assignment -name SEARCH_PATH ../../../common
    # ...
}
project_close
exit 0

Далее, для обеспечения полноты проекта необходимо сгенерировать модуль верхнего уровня. Ниже представлен скрипт, который это делает.

Скрипт mkcode.tcl


#Прописываем путь к библиотеке tcl
lappend auto_path "/usr/local/lib/tcllib1.21"
lappend auto_path "~/shcool/doc/reports/mytcllib"
lappend auto_path "./"
package require cmdline
package require mkverilog 1.0
#load_package flow
set options { \
        { "module_top.arg"  "" "Module top file" } \
    }
set usage "You need to specify options and values"
array set optshash [::cmdline::getoptions ::argv $options $usage]
puts "The module name is $optshash(module_top)"
# Создаем файл модуля
puts [mkverilog::mkcode $optshash(module_top)]
exit 0

Собственно, файл с кодом модуля на Verilog создает команда mkcode. Эта команда реализована в моей библиотеке и она очень проста. Так, что код этой команды я здесь приводить не буду.

Какая автоматизация без Makefile!

Продолжаем автоматизацию. Каждый раз набирать руками все эти скрипты, имена... это через некоторое время начинает утомлять. Можно ошибиться или что-нибудь подзабыть. Чтобы исключить, в этом контексте, человеческий фактор, разработаем Makefile. Требования к Makefile будут следующие:

  • имя проекта я определяю прямо в самом Makefile. Это не очень хорошо, зато стабильно, быстро и избавляет от некоторых лишних действий с переменными среды, аргументами;
  • имя топ модуля будет совпадать с именем проекта;
  • имена создаваемых директорий также определены в самом Makefile;
  • цели:
    • create - создание проекта и файла топ модуля
    • comp - компиляция проекта;
    • all - create & comp;
    • delete - уничтожить весть проект;
    • clean - очистить проект.

А вот и сам Makefile.

Makefile


PROJECT = dev_top 
MODULE_TOP = $(PROJECT)
VERILOG_SRC = $(PROJECT).v
WDIR = run
LIST_RM = $(WDIR)/*.rpt $(WDIR)/*.htm $(WDIR)/*.eqn $(WDIR)/*.pin $(WDIR)/*.pof db $(WDIR)/*.atm $(WDIR)/*.hdbx $(WDIR)/*.f $(WDIR)/*.map.* out atom_netlists $(WDIR)/db/* $(WDIR)/*.jdi $(WDIR)/*.done $(WDIR)/*.qxp $(WDIR)/*.sof $(WDIR)/*.pof $(WDIR)/*.sof $(WDIR)/*.fit.* $(WDIR)/*.sta.* $(WDIR)/*.sld 
################################################################### 
# Executable Configuration 
################################################################### 
MAP_ARGS = --family="MAX 10" 
FIT_ARGS = --part=10M50DAF484C7G 
ASM_ARGS = 
QSH = quartus_sh 

all: create comp
 
create: 
        $(QSH) -t ./mkproject.tcl -project $(PROJECT) -module_top $(MODULE_TOP)
        $(QSH) -t ./mkmodule.tcl -module_top $(MODULE_TOP) 

comp: 
        $(QSH) --prepare $(WDIR)/$(PROJECT)
        $(QSH) --flow compile $(WDIR)/$(PROJECT) 

delete:
        rm -rf $(WDIR)/db run 

clean: 
        rm -rf $(LIST_RM) 

.PHONY: all clean delete compile

"Вуаля" и make сделает всю работу.

Итоги

В этой части были рассмотрены несколько аспектов проектной деятельности. Проектирование модуля для связи по протоколу I2C со стороны ведомого устройства. Тестирование этого модуля в сборке с ведущим устройством. Также я рассмотрел некоторую автоматизацию на языке Tcl по созданию тестовых проектов. Заметил, что время потраченное на автоматизацию с лихвой окупается в процессе проектирования. И это замечательно.

Также я заметил такое правило, что что-то появляется там где этому уделяется внимание. Или, другим манером, происходит то, чему уделяется внимание. Так я уделил внимание вопросу автоматизации и появилась автоматизация. Кроме того появилось еще несколько идей как можно внести еще больший вклад в автоматизацию проектной деятельности. Это довольно интересно. Что-то делаешь и появляются новые идеи как можно сделать еще что-то. В ходе выполнения следующих задач, я думаю, будут реализованы некоторые из этих идей.

Все задачи были успешно выполнены и представлены на обсуждение читателям. Я представляю, что аудитория на данном сайте это в основном инженеры и мне хотелось бы услышать ваше мнение о проделанной работе. Также может кто-то выскажет свои собственные мысли по вопросам затронутым в этой и предыдущих статьях.

А я, в свою очередь, приступаю к следующему этапу работы. А именно к тестированию конструкций на конкретном железе.

 

      Мотивировать автора     

     Поддержать FPGA комьюнити     

Оставить комментарий/отзыв

 

1106
0
5.0

Всего комментариев : 0
avatar

FPGA-Systems – это живое, постоянно обновляемое и растущее сообщество.
Хочешь быть в курсе всех новостей и актуальных событий в области?
Подпишись на рассылку

ePN