September 16, 2011

Hello, Mono


    В универе надо делать лабы на C#, а сидеть под виндой совсем не хочется. Да и Visual Studio мне очень не нравится. Даже не смотря на то, что она у меня бесплатная, как для студента. Пришлось разбираться с Mono. Возни получилось больше не с Mono, а с gtk-sharp.

     Установка
    Поскольку под рукой у меня есть Ubuntu и Windows XP, то рассмотрим процесс установки всякого барахла именно для них (несмотря на то, что раздел скачивания на официальном сайте, а так же гугл никто не отменял).

   Установка для Ubuntu
    Установить нам надо три вещи:
  1. Непосредственно Mono
  2. Monodevelop
  3. Gtk-sharp для GUI
    Основных вариантов установки тут у нас целых три. Лично мне больше нравится 1-й.

    Вариант 1. Использование командной строки.
    Для установки вещей из пунктов 1 и 2, выполняем:
sudo apt-get remove mono-gmcs mono-gac mono-utils monodevelop monodoc-browser monodevelop-nunit monodevelop-versioncontrol mono-xsp
    Gtk-sharp можно установить так:
sudo apt-get install gtk-sharp2
    Вариант 2. Использование Ubuntu Software Center.
    В поиск вводим "monodevelop", устанавливаем и получаем почти такой же букет, как в 1-м варианте, но без gtk-sharp.  Для того, чтобы это исправить в поиск вводим "gtk sharp" и скачиваем "GTK# 2.10 suite".
    
    Вариант 3. Собери сам, "будь мужиком".
    Поповоду исходников самого Mono для Ubuntu здесь я толком ничего не нашел. Только упоминание про Synaptic. Исходники Monodevelop здесь, а gtk-sharp здесь, внизу справа.

    Установка для Windows
    Ставить Mono под Windows при существовании Visual Studio, наверное, является мазохизмом. Скачать его можно здесь, но, вероятно, максимум что понадобится, так это "Gtk# for .NET". Вот его-то и можно скачать для того, чтобы приложения с GUI, разработанными с помощью Gtk-sharp, запускались под виндой (при наличии .NET, конечно).

    Первый проект
    В качастве первого проекта будет приложение с Gtk-GUI, позволяющее выполнять простейшие операции со списком граждан, а именно добавление и удаление. Пример работы приложения на рисунке ниже.


    Компоненты, примененные в GUI:
  1. HPaned
  2. VBox
  3. Label
  4. Entry
  5. Button
  6. TreeView
  7. Dialog
  8. Menu
   Данные о гражданах хранятся в объектах класса Citizen, помещенных в System.Collections.Generic.LinkedList<Citizen>. Добавление происходит по нажатию на кнопку "Add", а удаление - по выбору пункта "Delete" в выпадающем меню. Класс Citizen:
namespace lab_01{

    public class Citizen{

        public string name;
        public string passportCode;
        public string nationality;
        
        public Citizen (
                string name,
                string passportCode,
                string nationality){
            this.name = name;
            this.passportCode = passportCode;
            this.nationality  = nationality;
        }
    }
}
   Все исходники проекта размещаются внизу сообщения, а здесь рассмотрим только основные мометны создания GUI с помощью Gtk-sharp, примененные в проекте.

     PopupMenu
    Эта штука доставила больше всего проблем. Для того, чтобы прикрутить её к TreeView пришлось потратить почти два вечера. Это правда. Я сам в шоке.
   Дело в том, что у объектов, классы которых наслебуются от Gtk.Widget есть сигнал "PopupMenu". Казалось бы, что в нем кроется решение. Но это не так, что и сбивает с толку. Этот сигнал в конечном итоге оказался вообще не при чем. Его нужно использовать, если вызывать контекстное меню по сочетанию клавиш, например, "Shift+F10". Для событий от мыши есть сигналы "ButtonPressEvent" и "ButtonReleaseEvent". Т.к. первый работать не хотел, то пришлось использовать второй.
    Добавить сигнал можно 2-мя способами:
    1. При редактировании GUI в свойствах объекта во вкладке "Signals" выбрать необходимый сигнал, указать имя обработчика событий и 2 раза нажать на название сигнала. После этого в файле класса, где описан объект появится запись наподобие этой:
this.citizensView.ButtonReleaseEvent += 
    new Gtk.ButtonReleaseEventHandler(
        this.CitizensViewButtonReleaseEventHandler);
    Сам файл класса, например "MainWindow.cs",  будет размещаться в подкаталоге проекта "gtk-gui".
     2. Сигнал так же можно добавить вручную, в каком-нибудь методе инициализации или конструкторе, записав практически то же самое, что и в строке выше ("практически", потому, что зависит от того, в каком классе строка будет размещаться).
  Здесь у нас citizensView - это Gtk.TreeView, а CitizensViewButtonReleaseEventHandler - название метода-обработчика сигнала:
    private static int MOUSE_RIGHT_BUTTON_CODE = 3;
    private PopupMenu citizensViewPopupMenu;
    
    ................

    protected virtual void CitizensViewButtonReleaseEventHandler (
            object o, Gtk.ButtonReleaseEventArgs args){
        if (RightMouseWasClicked(args.Event)){    
            citizensViewPopupMenu.doPopup();
        }
    }

    protected bool RightMouseWasClicked(Gdk.EventButton evt){
        return evt.Button==MOUSE_RIGHT_BUTTON_CODE;
    }
    MOUSE_RIGHT_BUTTON_CODE - код правой кнопки мыши.
    Класс PopupMenu:
using Gtk;
using System;

namespace lab_01{

    public class PopupMenu{

        Menu popupMenu = new Menu();
        
        public PopupMenu (){
        }
        
        public void addItemByTitleWithHandler(
                string itemTitle, EventHandler handler){
            MenuItem item = new MenuItem(itemTitle);
            item.Activated += handler;
            item.Show();
        
            popupMenu.Append(item);
        }
        
        public void doPopup(){
            popupMenu.Popup(
                null,null,null,3,Gtk.Global.CurrentEventTime);
        }
    }
}
    Инициализация PopupMenu:
    private static string TITLE_DELETE = "Delete";

    ................


    protected void CitizensViewPopupInit(){
        citizensViewPopupMenu = new PopupMenu();
        EventHandler deleteHandler = 
            new EventHandler(CitizensViewDeleteEventHandler);
        citizensViewPopupMenu.addItemByTitleWithHandler(
            TITLE_DELETE, deleteHandler);
    }

    ................

    protected void CitizensViewDeleteEventHandler(
            object sender, EventArgs e){
        DeleteSelectedCitizen();
    }
    TreeView 
   Подробное использование этого класса описано здесь. Остановимся только на основных моментах. 
     1. Инициализация.
  За инициализацию CitizensView в проекте отвечает класс "CitizensViewInitializer". В нем создается модель для TreeView, добавляются колонки, заголовки для них, а так же происходит связывание колонок с аттрибутами класа "Citizen". Рассмотрим код создания модели и инициализации колонки "Name":
    private static string COLUMN_NAME_TITLE = "Name";
    
    ................

    public void doInit(){
            CitizensViewModelInit();
            CitizensViewColumnsInit();
    }
        
    private void CitizensViewModelInit(){
        Gtk.ListStore listStoreModel = 
            new Gtk.ListStore (typeof (Citizen));
        citizensView.Model = listStoreModel;
    }
        
    private void CitizensViewColumnsInit(){
        CitizensViewColumnNameInit();
        CitizensViewColumnPassportCodeInit();
        CitizensViewColumnNationalityInit();
    }
    
    private void CitizensViewColumnNameInit(){
        Gtk.TreeViewColumn nameColumn = new Gtk.TreeViewColumn ();
        Gtk.CellRendererText nameCell = new Gtk.CellRendererText ();
        
        nameColumn.Title = COLUMN_NAME_TITLE;
            
        nameColumn.PackStart (nameCell, true);
        nameColumn.SetCellDataFunc (
            nameCell, new Gtk.TreeCellDataFunc (RenderCitizenName));
            
        citizensView.AppendColumn (nameColumn);
    }
        
    private void RenderCitizenName (
            Gtk.TreeViewColumn column,
            Gtk.CellRenderer cell,
            Gtk.TreeModel model,
            Gtk.TreeIter iter){
        Citizen citizen = (Citizen) model.GetValue (iter, 0);
        (cell as Gtk.CellRendererText).Text = citizen.name;
    }
    
    ................
     Отображение содержимого LinkedList
    protected void RefreshCitizensList(){
        Gtk.ListStore model = (Gtk.ListStore)citizensView.Model;
        model.Clear();
        ShowUpCitizensList(model);
    }
    
    protected void ShowUpCitizensList(Gtk.ListStore model){
        foreach (Citizen citizen in citizensList){
            model.AppendValues(citizen);
        }
    }
    Получение объекта по выбранной строке в TreeView
    Получение объекта по выбранной строке продемонстрировано в методе DeleteSelectedCitizen:
    private static string MESSAGE_SELECT_ITEM =
        "Select something to delete, please!";

    .................

    protected void DeleteSelectedCitizen(){
        TreeSelection selection = citizensView.Selection;
        Gtk.TreeIter iter;
        Gtk.TreeModel model;
        selection.GetSelected(out model, out iter);
            
        if (NothingIsSelected(iter)){
            MessageUI.ShowMessage(MESSAGE_SELECT_ITEM, this);
        } else {
            Citizen citizen = (Citizen)model.GetValue(iter, 0);        
            citizensList.Remove(citizen);
            RefreshCitizensList();
        }
    }
    
    protected bool NothingIsSelected(Gtk.TreeIter iter){
        return iter.Stamp==0;
    }
    Всплывающее сообщение (Notification)
    Метод "ShowMessage" я честно взял где-то после гугления. Добавил всего лишь параметр "Gtk.Window window". Вот описание метода:
public static void ShowMessage(string p, Gtk.Window window){
    Gtk.Dialog dlg =
        new Dialog("Message", window, DialogFlags.Modal);
    dlg.AddButton(Gtk.Stock.Ok, Gtk.ResponseType.Ok);
    dlg.DefaultResponse = Gtk.ResponseType.Ok;
        
    ScrolledWindow sw = new ScrolledWindow();
        
    Label l = new Label(p);
    l.WidthRequest = 285;
    l.Wrap = true;
            
    sw.AddWithViewport(l);
    
    dlg.VBox.PackStart(sw);
    dlg.SetDefaultSize(320, 160);
    
    dlg.ShowAll();
    dlg.Run();
    dlg.Destroy();
}
    Пример его работы:


Пример работы приложения под Windows:




September 10, 2011

Смерть от PowerPoint

    Вот [ссылка] на слайды, в которых даются советы о том, как свой доклад лучше преподносить аудитории, используя слайды.
    Больше информации в памятке докладчику.

Картинка взята [здесь]

September 03, 2011

Счетчик команд + внутренний стек

   Недавно возникла задача создать счетчик команд (instruction pointer, IP):

  Сигналы:
  1. I_CLK    -- сигнал синхросерии
  2. I_RST    -- сигнал сброса
  3. I_F      -- 3-х битная функция. Значения:        
            
    -- 000 : ожидание
             -- 001 : увеличение текущего адреса на 1
             -- 010 : текущий адрес+1 занести в стек
             --       установить текущий адрес равным
             --       I_ADDR
             -- 011 : взять текущее значение адреса из
             --       стека
             -- 100 : установить текущий адрес равным
             --       I_ADDR
             -- 101 : прибавить к текущему значению
             --       адреса значение смещения

  4. I_ADDR   -- входной 11-ти разрядный адрес перехода
  5. I_OFFSET -- входное 10-ти разрядное смещение
             -- старший бит отвечает за знак
  6. O_ADDR   -- выходной 11-ти разрядный адрес
    Структура счетчика без линий функции F выглядит так:


   Поначалу долго парился. Пытался как можно больше описать структурно. Получилось даже стек описать. Он даже правильно работал. Когда дошла очередь до управления стеком из счетчика команд, то начали тихонько плавиться мозги. Структурно связать несколько компонент не сложно. Сложно заставить это всё выдавать результат в течении одного цикла, а так же заставить одни компоненты управлять другими, когда все они имеют общий сигнал синхросерии. Наверно, не дорос я еще до такого. Короче, почувствовал себя УГ и решил описать с помощью поведенческой модели. Вот что вышло:

  1. ---------------------------------------------------------------------------------
  2. -- Instruction pointer with 32-depth stack
  3. -- F values:
  4. -- 000 : do nothing
  5. -- 001 : increment current address
  6. -- 010 : push current address+1 to stack. Set current address = I_ADDR
  7. -- 011 : pop value from stack to current address
  8. -- 100 : set current address = I_ADDR
  9. -- 101 : add to current address I_OFFSET value. I_OFFSET is signed
  10. ---------------------------------------------------------------------------------
  11. library IEEE;
  12. use IEEE.STD_LOGIC_1164.ALL;
  13. use IEEE.STD_LOGIC_ARITH.ALL;
  14. entity IP is
  15.     port (
  16.         I_CLK   :   in  STD_LOGIC;                      -- clock
  17.         I_RST   :   in  STD_LOGIC;                      -- reset
  18.         F       :   in  STD_LOGIC_VECTOR (2 downto 0);  -- function
  19.         I_ADDR  :   in  STD_LOGIC_VECTOR (10 downto 0); -- immediate address
  20.         I_OFFSET :  in  SIGNED (9 downto 0);            -- address' offset
  21.         O_ADDR  :   out STD_LOGIC_VECTOR (10 downto 0));-- output address
  22. end IP;
  23. architecture Behavioral of IP is
  24.    
  25.     type MATRIX32x11 is array(31 downto 0) of STD_LOGIC_VECTOR (10 downto 0);
  26.    
  27.     signal ST_body      : MATRIX32x11 := (others=> (others=>'0'));
  28.     signal ST_ptr       : integer range 0 to 31 := 0;
  29.    
  30.     signal L_addr       : STD_LOGIC_VECTOR (10 downto 0) := (others => '0');
  31.     signal L_addr_next  : STD_LOGIC_VECTOR (10 downto 0) := (others => '0');
  32. begin
  33.    
  34.     L_addr_proc: process(L_addr)
  35.     begin
  36.         L_addr_next <= conv_std_logic_vector(UNSIGNED(L_addr) + 1, 11);
  37.     end process;
  38.    
  39.     main: process(I_CLK, I_RST)
  40.     begin
  41.         if (I_RST='1') then
  42.             ST_ptr <= 0;
  43.             L_addr <= (others => '0');
  44.         elsif(I_CLK='1' and I_CLK'event) then
  45.             case f is
  46.                 when "001" => L_addr <= L_addr_next;
  47.                 when "010" =>
  48.                     ST_body(ST_ptr) <= L_addr_next;
  49.                     L_addr <= I_addr;
  50.                     ST_ptr <= ST_ptr + 1;
  51.                 when "011" =>
  52.                     L_addr <= ST_body(ST_ptr - 1);
  53.                     ST_ptr <= ST_ptr - 1;
  54.                 when "100" => L_addr <= I_addr;
  55.                 when "101" =>
  56.                     L_addr <= conv_std_logic_vector(SIGNED(L_addr) + I_OFFSET, 11);
  57.                 when others => NULL;
  58.             end case;
  59.         end if;
  60.     end process;
  61.    
  62.     O_addr <= L_addr;
  63. end Behavioral;

   Переполнение, опустошение стека, а так же выход за границы адресного пространства не учитываются. Результаты тестирования приведены ниже.


   Описание теста (по значениям управляющей функции):
  1. 0 : ожидание
  2. 1 : инкремент адреса
  3. 4 : установка адреса с I_ADDR (число 9)
  4. 1 : инкремент адреса
  5. 2 : запись следующего значения адреса в стек (число 14) и установка адреса с I_ADDR (число 9)
  6. 1 : инкремент адреса
  7. 3 : возврат адреса из стека (число 14)
  8. 1 : инкремент адреса
  9. 5 : прибавление к текущему абсолютному адресу смещение с I_OFFSET (число -4 в дополнительном коде, т.е. 1111111100)
  10. 1 : инкремент адреса
   Исходники можно забрать здесь.

Софтпроцессор. Сделай сам

   Решил поделится найденными ссылками, на различные материалы по созданию софтпроцессоров, а так же примеров их реализации. Возможно, со временем список будет расширяться.

 [1] Цикл лабораторных работ из КПИ, предоставляемых VHDL лабораторией им. Каневского. Цикл состоит из 7-ми лаб. Формат: PDF. Структура процессора, который должен получиться в результате, изображена ниже.

    Исходников нет. Есть только куски листинга в самих лабах. Кстати, у меня на руках была методичка - несколько старший бумажный вариант этих лаб. Так вот они несколько отличаются. И там и там есть небольшие куски, которых нет в другом варианте. Но во всяком случае, если делать все, как написано в лабах, то врядли что-то получится рабочее. Во-первых, потому, что там используется самодельная библиотека "CNetlist_Lib". В принципе это не беда, т.к. функции, описаные в ней, можно заменить стандартными, а недостающие коды архитектур компонент можно написать самому (к счастью есть листинги сигнатур). Во-вторых, там есть всякие "сюрпризы". Например, сначала в первых лабах подробно все описано, приведены листинги поведенческих и структурных описаний архитектур сущностей. Потом оказывается, что в конечном варианте сами сущности достаточно изменены, а описанию изменений достаточно внимания не отводится. Короче говоря, для студентов - самое то, но как учебник применить если и выйдет, то с натяжкой.

  [2]   Xilinx Picoblaze - фирменный opensource продукт от Xilinx. Бесплатно доступны исходники и документация (необходима только регистрация). Единственное, что не нравится - исходник по сути один и большой (почти 3000 строк), что затрудняет понимание того, что проиходит внутри. К тому же большая часть описана структурно с использованием 6-входовых LUT и триггеров. Идентифекаторы в коде более-менее читабельны, но все же сидеть и колупаться в 64-битных масках для LUT - дело не из увлекательных. Структура микроконтролера ниже.


  [3]  Лекция по созданию ЦП на opencores.org. Рассматривается создание микроконтроллера, схожего с Atmel ATmega8. Есть все исходники, документация и прочее (необходима регистрация для скачивания). Документация классная, единственное, чего реально не хватает, так это комментариев по коду или хотя бы описания сигналов. Структура результата вот:


 [4] Различные проекты на opencores.org (раздел "Processor"). Можно порыться и найти что-нибудь интересное. Главное, чтобы документация была нормальной, на английском. А то попадается то чисто на немецком, то на польском, то еще на каком-то.

  [5]  CPU Design HOW-TO - сборище ссылок всего и обо всем. Есть список бесплатных софтпроцессоров. Вообще от количества всяких ссылок глаза разбегаются.

Пачка лаб для SPARTAN 3E

    На сайте cosmiac.org есть пачка лаб для Xilinx Spartan 3E. Лабы заточены под Spartan 3E Starter Kit и Nexys 2 (большинство под Starter Kit). Всего лаб по плану 15, но 12-й (про термостат) нет.

    Предлагается выполить лабы 1-3, а далее можно делать любую. В первых лабах показано, как с помощью Xilinx ISE создавать проект, модуль vhdl, модуль тестирования, как синтезировать, имплементировать проект и прошивать ПЛИС. В состав лаб входит документация (pdf, doc) и исходники проекта.

Эволюция веба

   Вот ссылка на evolutionofweb.appspot.com, где коротко проиллюстрирована эволюция веб-технологий и браузеров. Помимо короткого описания есть и ссылки на статьи.