МІНІСТЕРСТВО ОСВІТИ І НАУКИ, МОЛОДІ І СПОРТУ УКРАЇНИ
НАЦІОНАЛЬНИЙ УНІВЕРСИТЕТ «ЛЬВІВСЬКА ПОЛІТЕХНІКА»
КАФЕДРА ЕОМ
ЗВІТ
до лабораторної роботи №3-4
з дисципліни «Комп’ютерні системи»
на тему:
«Аналіз програмної моделі процесу роботи арифметичного конвеєра»
Львів 2013
МЕТА ВИКОНАННЯ РОБОТИ
Навчитись здійснювати аналіз програмних моделей комп’ютерних систем, виконаних на мові System C.
ТЕОРЕТИЧІ ВІДОМОСТІ
Удосконалення елементної бази вже не дає кардинального росту продуктивності обчислювальної системи. Більш перспективними у цьому плані розглядаються архітектурні рішення, серед яких одне із найбільш значимих – конвеєризація.
Для пояснення ідеї конвеєра розглянемо малюнок 1, де показаний окремий функціональний блок (ФБ). Вихідні дані завантажуються у у вхідний регістр Ргвх , обробляється у функціональному блоці, а результат обробки фіксується у вихідному регістрі Ргвих. Якщо максимальний час обробки у ФБ дорівнює Тmax , то нові дані можуть бути занесені у вхідний регістр Ргвх не раніше, ніж через Тmax.
БПвх
®
ФБ1
®
БП1
®
ФБ2
®
БП2
®
ФБ3
®
БПвих
Рис. 1. Обробка інформації у конвеєрі з регістрами.
Тепер розподілемо функції, що виконуються у функціональному блоці ФБ на малюнку 1 між трьома послідовними незалежними блоками: ФБ1 , ФБ2 і ФБ3, причому так, що б максимальний час обробки у кожному ФБi був однаковий і дорівнював Tmax/3. Між блоками розмістимо буферні регістри Ргі, що призначені для збереження результату обробки у ФБі, на випадок, якщо наступний за ним функціональний блок ще не готовий використовувати цей результат.
У розглянутій схемі дані на вхід конвеєра можуть подаватися х інтервалом Tmax/3 (тобто, втричі частіше), і хоча затримка від моменту поступлення першої одиниці даних у Ргвх до моменту появи результату її обробки на виході Ргвих як і раніше складає Tmax, наступні результати з’являються на виході Ргвих вже з інтервалом Tmax/3.
На практиці рідко вдається добитися того, щоб затримки у кожному ФБі були однаковими. Як наслідок, продуктивність конвеєра знижується, оскільки період поступлення вхідних даних визначається максимальним часом їх обробки у кожному функціональному блоці. Для усунення цього недоліку або, в решті решт, часткової його компенсації кожний буферний регістр Ргі потрібно замінити буферною пам’яттю БПі , здатною зберігати множину даних і організований за принципом FIFO – “перший зайшов – перший вийшов”.
БПвх
®
ФБ1
®
БП1
®
ФБ2
®
БП2
®
ФБ3
®
БПвих
Рис. 2. Обробка інформації у конвеєрі з буферною пам’ятю.
Обробивши елемент даних, ФБі заносить результат у БПі, витягає з БПі-1 новий елемент даних і починає черговий цикл обробки, причому ця послідовність здійснюється кожним функціональним блоком незалежно від інших блоків. Обробка у кожному блоці може продовжуватися до тих пір, доки не беде ліквідована попередня черга або доки не буде переповнена наступна черга. Якщо емність буферної пам’яті достатньо велика, різниця у часі обробки не відбивається на продуктивності, тим не менш бажано, щоб середня тривалість обробки у всіх ФБі була однаковою.
ЗАВДАННЯ
1. Проаналізувати склад програмної моделі арифметичного конвеєра, (програма PIPE), яка виконана на мові System C.
2. Визначити інформаційні потоки у моделі арифметичного конвеєра.
3. Визначити зв’язки керування.
4. Накреслити блоки, з яких складається арифметичний конвеєр згідно поданої моделі.
5. Здійснити модернізацію функцій або параметрів арифметичного конвеєра, погодивши пропозицію з викладачем.
6. Накреслити структурну схему арифметичного конвеєра, яка відповідає програмній моделі, що аналізується.
Хід роботи
Процес встановлення та налаштування System C для VS 2010
Першим кроком потрібно завантажити найновішу версію System C з офіційного сайту розробника. [http://www.systemc.org]
Розпакувати System C (наприклад: на диск C:\), перейти в «C:\systemc-2.3.0\msvc80\SystemC» запустити SystemC.sln за допомогою Visual Studio 2010 і скомпілювати даний проект (клавіша F7). В результаті отримаємо в папці Debug файл systemc.lib
При створенні нового проекту System C потрібно в Visual Studio 2010 вибрати Новий проект -> Win32 Console Application (вибрати «пустий проект»).
Для налаштування потрібно перейти в Властивості проекту:
вкладка Властивості конфігурації – С\С++ – Мова, пункт: Включити інформацію про типи часу виконання(RTTI) встановити його значення “ТАК (/GR)”.
вкладка Властивості конфігурації – С\С++ – Препроцесор, в пункті Визначення препроцесора добавити шлях до розташування в System C папки src (наприклад: «C:\systemc-2.3.0\src»)
вкладка Властивості конфігурації – Каталоги VC++, в пункт Каталоги бібліотек добавити шлях розташування бібліотеки System C systemc.lib (наприклад: «C:\systemc-2.3.0\msvc80\SystemC\Debug»).
Добавити до проекту існуючі елементи (Проект –> Існуючі елементи) перейти за допомогою діалогового вікна в папку «C:\systemc-2.3.0\msvc80\SystemC\Debug» і добавити всі файли з розширенням *.obj .
Після цього видалити з проекту файл sc_isdb_trace.obj.
II. Виконання роботи
1. Лістинг програмної моделі арифметичного конвеєра на мові System C
------------------------<display.h>------------------------
#ifndef DISPLAY_H
#define DISPLAY_H
struct display : sc_module {
sc_in<double> in1; // input port 1
sc_in<double> in2;
sc_in<double> rez;
sc_in<bool> clk; // clock
void print_result(); // method to display input port values
//Constructor
SC_CTOR( display ) {
SC_METHOD( print_result ); // declare print_result as SC_METHOD and
dont_initialize();
sensitive << clk.pos(); // make it sensitive to positive clock edge
}
};
#endif
------------------------<display.cpp>------------------------
#include "systemc.h"
#include "display.h"
#include <stdio.h>
//Definition of print_result method
void display::print_result()
{
printf("INPUT1 = %f\t INPUT2 = %f\t Prev Result = %f\n", in1.read(),in2.read(),rez.read());
} // end of print method
------------------------<numgen.h>------------------------
#ifndef NUMGEN_H
#define NUMGEN_H
struct numgen : sc_module {
sc_out<double> out1; //output 1
sc_out<double> out2; //output 2
sc_in<bool> clk; //clock
// method to write values to the output ports
void generate();
//Constructor
SC_CTOR( numgen ) {
SC_METHOD( generate ); //Declare generate as SC_METHOD and
dont_initialize();
sensitive << clk.pos(); //make it sensitive to positive clock edge
}
};
#endif
------------------------<numgen.cpp>------------------------
#include "systemc.h"
#include "numgen.h"
// definition of the `generate' method
void numgen::generate()
{
static double a = 134.56;
static double b = 98.24;
a -= 1.5;
b -= 2.8;
out1.write(a);
out2.write(b);
} // end of `generate' method
------------------------<stage1.h>------------------------
#ifndef STAGE1_H
#define STAGE1_H
struct stage1 : sc_module {
sc_in<double> in1; //input 1
sc_in<double> in2; //input 2
sc_out<double> sum; //output 1
sc_out<double> diff; //output 2
sc_in<bool> clk; //clock
void addsub(); //method implementing functionality
//Counstructor
SC_CTOR( stage1 ) {
SC_METHOD( addsub ); //Declare addsub as SC_METHOD and
dont_initialize();
sensitive << clk.pos(); //make it sensitive to positive clock edge
}
};
#endif
------------------------<stage1.cpp>------------------------
#include "systemc.h"
#include "stage1.h"
//Definition of addsub method
void stage1::addsub()
{
double a;
double b;
a = in1.read();
b = in2.read();
sum.write(a+b);
diff.write(a-b);
} // end of addsub method
------------------------<stage2.h>------------------------
#ifndef STAGE2_H
#define STAGE2_H
struct stage2 : sc_module {
sc_in<double> sum; //input port 1
sc_in<double> diff; //input port 2
sc_out<double> prod; //output port 1
sc_out<double> quot; //output port 2
sc_in<bool> clk; //clock
void multdiv(); //method providing functionality
//Constructor
SC_CTOR( stage2 ) {
SC_METHOD( multdiv ); //Declare multdiv as SC_METHOD and
dont_initialize();
sensitive << clk.pos(); //make it sensitive to positive clock edge.
}
};
#endif
------------------------<stage2.cpp>------------------------
#include "systemc.h"
#include "stage2.h"
//definition of multdiv method
void stage2::multdiv()
{
double a;
double b;
a = sum.read();
b = diff.read();
if( b == 0 )
b = 5.0;
prod.write(a*b);
quot.write(a/b);
} // end of multdiv
------------------------<stage3.h>------------------------
#ifndef STAGE3_H
#define STAGE3_H
struct stage3: sc_module {
sc_in<double> prod; //input port 1
sc_in<double> quot; //input port 2
sc_out<double> powr; //output port 1
sc_in<bool> clk; //clock
void power(); //method implementing functionality
//Constructor
SC_CTOR( stage3 ){
SC_METHOD( power ); //declare power as SC_METHOD and
dont_initialize();
sensitive << clk.pos(); //make it sensitive to positive clock edge
}
};
#endif
------------------------<stage3.cpp>------------------------
#include <math.h>
#include "systemc.h"
#include "stage3.h"
//Definition of power method
void stage3::power()
{
double a;
double b;
double c;
a = prod.read();
b = quot.read();
c = (a>0 && b>0)? pow(a, b) : 0.;
powr.write(c);
} // end of power method
------------------------<stage_pikaso.h>------------------------
#ifndef STAGE_PIKASO_H
#define STAGE_PIKASO_H
struct stage_pikaso : sc_module {
sc_in<double> in1; //input 1
sc_in<double> in2; //input 2
sc_out<double> rez; //output 1
sc_in<bool> clk; //clock
void sqrt1(); //method implementing functionality
//Counstructor
SC_CTOR( stage_pikaso ) {
SC_METHOD( sqrt1 ); //Declare mod as SC_METHOD and
dont_initialize();
sensitive << clk.pos(); //make it sensitive to positive clock edge
}
};
#endif
------------------------<stage_pikaso.cpp>------------------------
#include "systemc.h"
#include "stage_pikaso.h"
void stage_pikaso::sqrt1()
{
double a;
double b;
double c;
a = in1.read();
b = in2.read();
if(a>b) c = sqrt(a);
else if(a<b) c= sqrt(b);
else c = sqrt(a);
rez.write(c);
}
------------------------<main.cpp>------------------------
#include "systemc.h"
#include "stage1.h"
#include "stage2.h"
#include "stage3.h"
#include "display.h"
#include "numgen.h"
#include "stage_pikaso.h"
#include <conio.h>
int sc_main(int, char *[])
{
//Signals
sc_signal<double> in1;
sc_signal<double> in2;
sc_signal<double> sum;
sc_signal<double> diff;
sc_signal<double> prod;
sc_signal<double> quot;
sc_signal<double> powr;
//pikaso
sc_signal<double> rez;
//Clock
sc_signal<bool> clk;
numgen N("numgen"); //instance of `numgen' module
N(in1, in2, clk ); //Positional port binding
stage1 S1("stage1"); //instance of `stage1' module
//Named port binding
S1.in1(in1);
S1.in2(in2);
S1.sum(sum);
S1.diff(diff);
S1.clk(clk);
stage2 S2("stage2"); //instance of `stage2' module
S2(sum, diff, prod, quot, clk ); //Positional port binding
stage3 S3("stage3"); //instance of `stage3' module
S3( prod, quot, powr, clk); //Positional port binding
/*oooooooooooooo_PIKASO_oooooooooooooooo*/
stage_pikaso Pik("stage_pikaso");
Pik.in1(in1);
Pik.in2(in2);
Pik.rez(rez);
Pik.clk(clk);
/*oooooooooooooo_PIKASO_oooooooooooooooo*/
display D("display"); //instance of `display' module
D(in1,in2,rez,clk); //Positional port binding
sc_start(0, SC_NS); //Initialize simulation
for(int i = 0; i < 50; i++){
clk.write(1);
sc_start( 10, SC_NS );
clk.write(0);
sc_start( 10, SC_NS );
}
_getch();
return 0;
}
2. Результат виконання PIPE до модифікацій
Рис.3. Скріншот результату виконання PIPE до змін, стандартний варіант
В програмі без змін виконується послідовна обробка сигналів в 3 етапи (stage1, stage2, stage3) на першому етапі два вхідні сигнали ( згенеровані numgen ) потрапляють на входи stage1, на його виходах отримаєм суму і різницю вхідних сигналів. Вихідні сигнали потрапляють на входи stage2, на його виходу отримуємо результати множення та ділення. Виходи stage2 з’єднані з входами stage3, на ньому здійснюється піднесення сигналу з першого входу до значення сигналу з другого. Результат отримаємо на виході stage3, який і показано на рис.3 для 50 різних наборів початкових значень сигналів.
3.Результат виконання PIPE після модифікації
Рис.4. Скріншот результату виконання PIPE після модифікації
При модифікації проекту PIPE мною було створено ще один паралельний вузол, який приймає ті ж самі вхідні сигнали, що й stage3, а на виході видає значення, яке рівне кореню квадратному числа, яке взяте з більшого значення вхідних сигналів. А також змінено вивід результатів на екран.
4. Описання блоків арифметичного конвеєра
Main
Це основний файл проекту PIPE, який включає ініціалізацію самого конвеєру, визначення портів, а також виклики інших блоків.
Numgen
Це генератор чисел, необхідний для встановлення вхідних значень на stage1. Початкові значення яких рівні: a = 134.56, b = 98.24. При кожному циклі змінюють своє значення на a - = 1.5 і b - = 2.8.
Display
Це файл який містить процедуру виводу інформації на екран, в моєму випадку входів і виходу створеного мною блоку stage_pikaso. Структура виводу наступна:
INPUT1 = %f\t INPUT2 = %f\t Prev Result = %f\n
Вхід 1 Вхід 2 Результат
Stage1
Це модуль, що виконує операції додавання та віднімання, тобто на одному виході цього блоку буде сума 1 і 2 сигналів, а на 2 виході їх різниця.
Stage2
це модуль, що виконує операції множення та ділення, тобто на одному виході цього блоку буде результат ділення, а на другому – результат множення. Ділення на 0 заборонене, тому перед тим як виконати дію ділення треба це перевірити:
if( b == 0 )
b = 5.0;
В результаті, коли дільник таки рівний 0 присвоємо дільнику значення 5.
Stage3
Це модуль виконує операцію піднесення до степення. Даний блок на виході може повертати число, яке відповідає результату піднесення до степеня сигналу з входу 1 до сигналу з входу 2 , або 0, якщо умова сигнал перший і сигнал другий більше нуля не виконується.
Stage_pikaso
це створений мною модуль, паралельний до модуля stage1, що виконує операцію кореня квадратного більшого з двох вхідних значень сигналів, при умові якщо вони рівні береться значення першого сигналу.
5. Структурнa схема арифметичного конвеєра
6. Модернізація арифметичного конвеєра
Арифметичний конвеєр був розширений додатковим блоком stage_pikaso. Його лістинг:
Висновок
В даній лабораторній роботі я навчився здійснювати аналіз програмних моделей комп’ютерних систем, виконаних на мові System C. Провів розширення арифметичного конвеєру.