Теперь уже работает – после нескольких исправлений в коде проекта Amber. Сейчас программа hello-world, написаннная на языке C и откомпилированная для ARM запускается в плате Марсоход2. Программа печатает сообщение функцией printf(..), которая отсылает его нам через последовательный порт на скорости 921600 бит/сек. А дело было так.
После симуляции проекта в Icarus Verilog я убедился, что сам проект Amber рабочий (вообще было бы странно, если бы он был нерабочим). Но проблема оставалась – в моем проекте Quartus II для ПЛИС Altera что-то видимо было сделано не верно.
Я использовал программу Altera Quartus II SignalTap, чтобы заглянуть внутрь ПЛИС. Теперь у меня была возможность сравнивать сигналы в симуляции с теми сигналами, которые я могу захватить логическим анализатором SignalTap.
Мой проект сделан таким образом, что вход с кнопочки (той, что ближе к разъему USB) платы Марсоход2 является сигналом сброса brd_n_rst для проекта. Правда у самого процессора Amber сброса нет, но зато есть сигнал i_system_rdy, который я и связал со сбросом. Таким образом, процессор должен стартовать после того, как сигнал сброса будет снят. Это дает возможности для использования логического анализатора на самом старте процессора.
В SignalTap я задаю исследуемые сигналы, например внешние сигналы процессора к шине Wishbone bus. Я хочу посмотреть как процессор читает код программы из bootrom. Начальный момент захвата для логического анализатора (trigger) – это сигнал процесора i_system_rdy. Анализ внутренних сигналов в момент старта процессора производится следующим образом:
- компилирую проект с включенным логическим анализатором и выбранными сигналами для анализа.
- запускаю SignalTap.
- на плате нажимаю кнопочку сброса для проекта Amber.
- из SignalTap загружаю проект в ПЛИС – при этом проект не стартует, так как я держу кнопку сброса.
- в SignalTap запускаю логический анализатор, который «подвисает» ожидая сигнала триггера на процессоре i_system_rdy.
- отпускаю кнопочку сброса на плате – SignalTap тут же выдает мне картинку, как выглядят мои сигналы внутри ПЛИС Cyclone III.
Используя этот метод я смог убедиться, что процессор на старте скорее мертв, чем жив. На шине Wishbone bus не происходит ничего, хотя адрес на bootrom выдается правильный – это ноль и с bootrom поступает первая команда процессора ARM 0xE3A00000 (это mov r0, #0x00000000). Другие сигналы шины o_wb_cyc, o_wb_stb, i_wb_ack не шевелятся.
Пришлось довольно долго повозиться рассматривая откуда эти сигналы берутся и от чего зависят. Каждая итерация занимает довольно много времени, поскольку, если я меняю список исследуемых сигналов в SignalTap мне требуется перекомпилировать проект.
В конце концов я добрался до одной очень странной странности – в модуле hw/vlog/amber23/a23_cache.v есть так называемая «машина состояний» (StateMachine) и похоже, что ее состояние при старте системы изначально не верное.
Видите, что начальное состояние машины состояний в кэш контроллере c_state.CS_INIT = 1, но ведь должно быть не так! В коде Verilog a23_cache.v написано ведь совсем другое:
reg [3:0] c_state = CS_IDLE;
Более того, если зайти в Quartus II в меню Tools => Netlist Viewers => State Machine Viewer то можно видеть, что компилятор Quartus II действительно неправильно определил поведение описанной на языке Verilog машины состояний.
Quartus II считает, что у этой машины начальное состояние это CS_INIT, а должно быть CS_IDLE.
На самом деле мне трудно утверждать, что это именно баг компилятора Quartus II, возможно есть какие-то тонкости или неопределенности в стандарте языка Verilog. Я не являюсь досконольным знатоком спецификации Verilog. Все, что я утверждаю – написанное не соответствует скомпилированному и синтезированному в ПЛИС.
Очень странно, что даже явное указание начального значения для регистра c_state не меняет ситуацию, я написал вот так:
reg [3:0] c_state = CS_IDLE;
initial
c_state = CS_IDLE;
Но и это не помогло. Все из-за того, что, видимо, регистр c_state интерпретируется компилятором Quartus II каким-то особым образом из-за того, что описывает машину состояний. Очень странный баг (или фича?) в Quartus II.
В принципе в Quartus II есть множество настроек связанных с особенностями компиляции. Посмотреть их можно через меню Assignments => Settings и далее в диалоговом окне настройки Analysis & Synthesis Settings и далее кнопка More Settings.
Здесь в списке есть Ignore Verilog initial constructs и у меня, как положенно, стоит здесь Off. Тоесть я хочу, чтобы initial constructs работали, чтобы в проекте при старте в регистрах были заданные в проекте значения.
В общем, помучавшись немного, я обнаружил здесь же в настройках другую полезную опцию – Extract Verilog State Machines – здесь стояло On. Это как раз та опция, которая заставляла компилятор Quartus интерпретировать регистр c_state не как обычный регистр, а как специфический регистр машины состояний. Как написано в подсказке Quartus эта опция позволяет:
Allows the Compiler to extract state machines from Verilog Design Files. The Compiler optimizes state machines using special techniques to reduce area and/or improve performance. If set to Off, the Compiler extracts and optimizes state machines in Verilog Design Files as regular logic.
Ага, как-же, дооптимизировали area и performance, что проект не правильно работает. В общем, выключил я эту опцию в настройках. Теперь Extract Verilog State Machines => Off.
Вы знаете, стало лучше, но не намного. Сигналы на Wishbone bus стали хоть как-то меняться, но все равно процессор не работает:
После этого, я нашел еще одно место в проекте Amber неправильно интерпретируемое компилятором Quartus II – это в файле a23_decode.v
В этом файле выходные сигналы модуля объявляются и одновременно задается их начальное значение, вот так:
module a23_decode
(
input i_clk,
input [31:0] i_read_data,
...................
output reg [31:0] o_read_data = 1'd0,
output reg [4:0] o_read_data_alignment = 1'd0, // 2 LSBs of read address used for calculating shift in LDRB ops
output reg [31:0] o_imm32 = 'd0,
output reg [4:0] o_imm_shift_amount = 'd0,
output reg o_shift_imm_zero = 'd0,
output reg [3:0] o_condition = 4'he, // 4'he = al
output reg o_exclusive_exec = 'd0, // exclusive access request ( swap instruction )
output reg o_data_access_exec = 'd0, // high means the memory access is a read
// read or write, low for instruction
output reg [1:0] o_status_bits_mode = 2'b11, // SVC
output reg o_status_bits_irq_mask = 1'd1,
output reg o_status_bits_firq_mask = 1'd1,
.................
Так вот получается, что все эти начальные значения для output reg проигнорированны компилятором. Я не смог найти опцию в настройках Quartus II, которая бы заставила использовать эти назначенные значения.
Пришлось ниже в тексте модуля задавать их явно с помощью конструкции initial.
Вот только после всех этих манипуляций с настройками проекта и изменений в коде a23_decode.v проект заработал и в консоли последовательного порта я увидел заветную фразу “Marsohod2: Hello, World!”
Теперь и в SignalTap я вижу активный процесс чтения кода процессором из bootrom:
Да и сообщение в консоли последовательного порта - это явное доказательство работоспособности проекта.
Вот теперь его можно и дальше развивать.
В общем, оказалось, что проект Amber использует какие-то конструкции языка Verilog, которые интерпретируются компилятором Quartus II несколько специфическим образом. Тем не менее, мы смогли это обойти исправив настройки проекта Altera Quartus II и несколько модифицировав исходный текст процессора Amber.
Все последние изменения в проекте есть на GitHub в нашем разделе https://github.com/marsohod4you/Amber-Marsohod2.
Подробнее...