Журнал LinuxFormat - перейти на главную

LXF162:CPU своими руками

Материал из Linuxformat
Перейти к: навигация, поиск


Содержание

CPU свои­ми ру­ка­ми

Часть вто­рая

Еще два ком­по­нен­та элек­трон­но­го серд­ца ва­ше­го CPU: Джо­на­тан Ро­бертс под­во­дит вас на шаг бли­же к ра­бо­че­му про­цес­со­ру.

В LXF158 мы по­ка­за­ли вам, как по­стро­ить соб­ст­вен­ный ком­пь­ю­тер из ба­зо­вых элек­трон­ных бло­ков. По су­ти, мы из­го­то­ви­ли дво­ич­ный сум­ма­тор. Мы за­кон­чи­ли ста­тью, ска­зав, что соз­дан­ное на­ми — это еще да­ле­ко не на­стоя­щий ком­пь­ю­тер, и вам нуж­ны еще несколь­ко ком­понен­тов, пре­ж­де чем ва­ша ма­ши­на смо­жет пре­тен­до­вать на звание ком­пь­ю­те­ра.

В этой ста­тье мы рас­смот­рим дальней­шие ша­ги и пред­ста­вим вам еще несколь­ко ком­понен­тов, про­де­мон­ст­ри­ро­вав, как рас­ши­рить воз­мож­но­сти ва­ше­го сум­ма­то­ра до вы­полнения дру­гих дей­ст­вий (AND [И], XOR [ИСКЛ.ИЛИ] и NOT [ИЛИ]) и соз­дать це­пи па­мя­ти для хранения вво­да и вы­во­да.

Пе­ред чтением этой ста­тьи вам сто­ит про­честь пре­ды­ду­щую часть, что­бы осве­жить па­мять, по­сколь­ку мы ис­хо­дим из то­го, что вы с ней оз­на­ком­ле­ны. Ес­ли у вас нет эк­зем­п­ля­ра LXF158, вы смо­же­те най­ти PDF этой ста­тьи на дис­ке это­го ме­ся­ца.

Де­ко­дер

LXF162.feat cpu.figure6 opt.png
LXF162.feat cpu.figure7.png

Пер­вый ком­понент, ко­то­рый мы рас­смот­рим – это де­ко­дер, или, точнее, линей­ный де­ко­дер. Он очень прост, но очень удо­бен в ра­бо­те и сам по се­бе, и де­ко­де­ры так­же ис­поль­зу­ют­ся в муль­ти­п­лек­со­рах – что ис­клю­чи­тель­но важ­но для осталь­ной час­ти на­шей ста­тьи.

По су­ти, все де­ко­де­ры по­зво­ля­ют сде­лать вы­бор из мно­же­ст­ва вы­хо­дов, ис­поль­зуя толь­ко по­ло­ви­ну вхо­дов. Бо­лее кон­крет­но – при един­ст­вен­ном вхо­де де­ко­дер по­зво­лит вам вы­би­рать ме­ж­ду дву­мя вы­хо­да­ми, два вхо­да обес­пе­чат вам вы­бор из че­ты­рех вы­хо­дов, и т. д. В слу­чае де­ко­де­ров мы го­во­рим не о вхо­де и вы­хо­де, а об ад­рес­ной линии и линии пе­ре­да­чи дан­ных. Таб­ли­ца ис­тин­но­сти для про­сто­го линей­но­го де­ко­де­ра 1-в-2 раз­ме­ще­на ниже, а схе­ма со­единений при­ве­де­на на рис. 1.

(thumbnail)
Рис. 1. На этой схе­ме вы ви­ди­те ли­ней­ный де­ко­дер 1-в-2 сле­ва и муль­ти­п­лек­сор 2-в-1 спра­ва. Ус­лов­ное обо­зна­че­ние ни­же — это встро­ен­ное пред­став­ле­ние KTechlab, где X0 и X1 — эк­ви­ва­лен­ты I0 и I1.

Как ви­ди­те, все очень про­сто. Ад­рес­ная линия раз­двое­на, и вход NOT раз­ме­щен на од­ном от­ветв­лении это­го раз­двоения. Та­ким об­ра­зом, толь­ко од­на из этих линий пе­ре­да­чи дан­ных мо­жет за один раз пе­ре­дать 1.

Муль­ти­п­лек­со­ры

Муль­ти­п­лек­сор по­до­бен де­ко­де­ру, но спо­со­бен на боль­шее. Вме­сто вы­бо­ра ме­ж­ду дву­мя линия­ми пе­ре­да­чи дан­ных, он ис­поль­зу­ет ад­рес­ную линию для вы­бо­ра ме­ж­ду дву­мя вхо­да­ми, и вы­бран­ная ве­ли­чи­на (0 или 1) бу­дет пе­ре­на­прав­ле­на на един­ст­вен­ную линию пе­ре­да­чи дан­ных.

Его таб­ли­ца ис­тин­но­сти приведена ниже, а его схе­му со­единений вы так­же мо­же­те уви­деть на рис. 1. На этой схе­ме вы ви­ди­те, что муль­ти­п­лек­сор – это про­сто рас­ши­рен­ный де­ко­дер.

Боль­ше ло­ги­ки

(thumbnail)
Рис. 2. АЛУ, ис­поль­зую­щее муль­ти­п­лек­со­ры для обес­пе­че­ния вы­бо­ра ме­ж­ду опе­ра­ция­ми.

Что­бы это про­де­мон­ст­ри­ро­вать, мы ском­биниру­ем наш пол­ный сум­ма­тор с про­шло­го раза с дру­ги­ми ло­ги­че­­ски­­ми схе­ма­ми (AND, OR и XOR), и ис­поль­зу­ем муль­ти­п­лек­сор для ука­зания опе­ра­ций, ко­то­рые тре­бу­ет­ся вы­полнить.

Пер­вое, что нуж­но сде­лать – соз­дать ло­ги­че­скую схе­му. Это про­сто, по­сколь­ку, что­бы вы­полнить AND для двух дво­ич­ных чи­сел, все, что вам нуж­но сде­лать – это со­единить ка­ж­дый из пер­вых би­тов с вхо­дом AND, вто­рые би­ты – с дру­гим, тре­тьи – с дру­гим, и так да­лее. То же вер­но и для опе­ра­ций OR и XOR. По­ка что мо­же­те оста­вить вы­хо­ды несо­единен­ны­ми.

При­смот­рев­шись внима­тельнее, вы смо­же­те уви­деть все это на рис. 2. Од­на­ко помните, что KTechlab не очень хо­ро­шо ра­бо­та­ет со слож­ны­ми схе­ма­ми, по­доб­ны­ми этой, и будь­те внима­тель­ны, ри­суя со­единения – про­ще все­го бу­дет пе­ре­вес­ти это в ре­жим Manual, вы­брав нуж­ную оп­цию под знач­ком с крас­ной руч­кой.

Сде­лав это, соз­дай­те че­ты­ре муль­ти­п­лек­со­ра, на­стро­ив дли­ну ад­ре­са ка­ж­до­го на два. Это мож­но сде­лать, щелк­нув по муль­ти­п­лек­со­ру, за­тем за­гля­нув в поя­вив­шую­ся панель спра­ва (Item Editor). По­лу­чит­ся муль­ти­п­лек­сор с че­тырь­мя вхо­да­ми, ме­ж­ду ко­то­ры­ми вы смо­же­те вы­би­рать, соз­да­вая раз­ные ком­би­на­ции двух ад­рес­ных линий.

Да­лее, со­едините пер­вый вход AND, вход OR, вход XOR и сум­ма­тор с пер­вым муль­ти­п­лек­со­ром, вто­рой вход – со вто­рым муль­ти­п­лек­со­ром, и так да­лее. За­тем со­едините ло­ги­че­­ские вы­во­ды с со­единением муль­ти­п­лек­со­ра, от­ме­чен­ным x, и, на­конец, при­сое­дините еще два ло­ги­че­­ских вво­да: один – ко всем A0, и дру­гой – ко всем A1 на муль­ти­п­лек­со­рах.

Все это по­ка­за­но на рис. 2, хо­тя вы мо­же­те так­же за­гру­зить схе­му с дис­ка – ALU.circuit – и рас­смот­реть ее хо­ро­шень­ко. Важ­ная под­сказ­ка: все долж­но быть вы­ровнено, по­то­му что начнет­ся боль­шая пу­таница, ес­ли би­ты од­но­го и дру­го­го вхо­дов пой­дут в раз­ном по­ряд­ке, а по­ря­док вы­ход­ных би­тов то­же ока­жет­ся дру­гим!

Рас­смот­рев схе­му по­бли­же, вы уви­ди­те ря­дом с ад­рес­ны­ми вхо­да­ми спи­сок зна­чений, на ко­то­рые они долж­ны быть на­строе­ны для вы­бо­ра раз­ных опе­ра­ций. В на­стоя­щем ком­пь­ю­те­ре это име­ну­ет­ся opcodes (со­кра­щение от «ко­ды опе­ра­ций»). Их нуж­но пе­ре­дать про­цес­со­ру, что­бы за­ста­вить его вы­полнить оп­ре­де­лен­ное дей­ст­вие. Ря­дом с ко­да­ми на­хо­дят­ся трех­бу­к­вен­ные опи­сания функ­ций – это мнемоника, для про­сто­ты за­по­ми­нания и че­ло­ве­ко-чи­тае­мо­го опи­сания то­го, что де­ла­ют раз­ные ко­ды. Ес­ли вы когда-нибудь пи­са­ли на язы­ке ас­семб­ле­ра, то это как раз и есть мнемониче­­ские зна­чения, ко­то­рые ком­пи­ля­то­ры ас­семб­ле­ра за­тем пре­вра­ща­ют в дво­ич­ные эк­ви­ва­лен­ты.

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

А ес­ли мы хо­тим, что­бы вы­ход не про­сто ис­че­зал, но пре­вра­щал­ся во вход сле­дую­ще­го уст­рой­ст­ва? Или ес­ли мы хо­тим за­про­грам­ми­ро­вать вхо­ды за­ранее?

Об­рат­ная связь

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

Возь­мем обыч­ный вен­тиль OR. На­помним его таб­ли­цу ис­тин­но­сти:

LXF162.feat cpu.figure8.png

Вен­тиль OR бу­дет га­ран­ти­ро­ван­но вес­ти се­бя имен­но так, ес­ли толь­ко... Что про­изой­дет, ес­ли под­клю­чить вы­ход ко вхо­ду? Что ес­ли соз­дать сиг­нал об­рат­ной свя­зи?

По­смот­ри­те на схе­му на рис. 3 сле­ва. Здесь вы ви­ди­те, как мы от­вет­ви­ли вы­ход и со­единили его на­пря­мую с одним из вхо­дов. Ос­тав­ший­ся вход по-прежнему при­сое­динен к на­ше­му ло­ги­че­­ско­­му вен­ти­лю.

По­ка остав­ший­ся вход – это 0, все уст­рой­ст­во оста­ет­ся в этом со­стоянии. Од­на­ко сто­ит вам пе­ре­клю­чить вход на 1, как уст­рой­ст­во за­бло­ки­ру­ет­ся. Вы­ход бу­дет по­сто­ян­но пе­ре­да­вать 1 об­рат­но на вход, и из это­го со­стояния бу­дет не вый­ти. Об­раз­но вы­ра­жа­ясь, вен­тиль за­перт. По­про­буй­те соз­дать эту схе­му, что­бы по­нять, как она ра­бо­та­ет.

Триг­гер за­держ­ки

Оче­вид­но, что этой про­стой схе­мы об­рат­ной свя­зи недоста­точ­но для при­менения в ка­че­­ст­ве ячей­ки па­мя­ти – за­дав это со­стояние, вы не смо­же­те пе­реуста­но­вить его! Ес­ли вы за­пи­са­ли ту­да пор­цию дан­ных, а за­тем ре­ши­ли за­пи­сать еще, то вам не по­вез­ло. Есть дру­гие схе­мы, ко­то­рые ис­поль­зу­ют тот же прин­цип об­рат­ной свя­зи, но без та­ко­го ог­раничения. Од­на из них из­вест­на под на­званием «триг­гер D-ти­па» или триг­гер за­держ­ки, и мы его ис­поль­зу­ем, что­бы про­де­мон­ст­ри­ро­вать, как в ком­пь­ю­те­рах ра­бо­та­ет па­мять.

Взгляните на схе­му в пра­вой час­ти рис. 3. Это про­ме­жу­точ­ный шаг ме­ж­ду на­шей про­стой схе­мой об­рат­ной свя­зи и триг­ге­ром за­держ­ки. Он из­вес­тен под на­званием «за­кры­тый триг­гер S-R» или триг­гер пе­ре­сче­та. Ес­ли в этой схе­ме оба вхо­да уста­нов­ле­ны в нуль (zero), вы­хо­ды оста­ют­ся бло­ки­ро­ван­ны­ми – ины­ми сло­ва­ми, они пом­нят свое пре­ды­ду­щее со­стояние. Ес­ли верхний вход уста­нов­лен в 1, то нижний вы­ход бу­дет 1; а ес­ли ниж­ний вход ра­вен 1, верхний вы­ход ра­вен 1.

(thumbnail)
Рис. 3. Схе­ма об­рат­ной свя­зи сле­ва де­мон­ст­ри­ру­ет ос­нов­ной прин­цип циф­ро­вой па­мя­ти, а триг­гер пе­ре­сче­та спра­ва по­ка­зы­ва­ет, как по­лу­чить боль­ший кон­троль над схе­мой.

Идея этой схе­мы в том, что два вы­хо­да всегда долж­ны быть в про­ти­во­по­лож­ных со­стояниях. Од­на­ко так бу­дет не всегда: ес­ли од­но­вре­мен­но за­дать на оба вхо­да 1, оба вы­хо­да ста­нут рав­ны 0. Эту си­туа­цию мож­но обой­ти, соз­дав триг­гер за­держ­ки, ко­то­рый так­же да­ет нам боль­шую сте­пень кон­тро­ля за опе­ра­ция­ми схе­мы. Один из них мож­но уви­деть на рис. 4.

Са­мая важ­ная функ­ция в этой схе­ме на те­ку­щий мо­мент в том, что, со­еди­няя вхо­ды в еди­ное управ­ление и ин­вер­ти­руя его, мы обес­пе­чи­ва­ем та­кую си­туа­цию, когда схе­ма не мо­жет ока­зать­ся в нештат­ном по­ло­жении. Вто­рое, что сто­ит от­ме­тить, это ис­поль­зо­вание вен­ти­ля AND для «пе­ре­кры­тия» схе­мы. Это обес­пе­чи­ва­ет вто­рой вход, ко­то­рый дол­жен быть пе­ре­клю­чен на 1, что­бы ак­ти­ви­ро­вать схе­му. Когда она ак­ти­ви­ру­ет­ся, верх­ний вы­ход бу­дет всегда от­ра­жать вход, но когда она от­клю­че­на, схе­ма останет­ся бло­ки­ро­ван­ной, что бы вы ни де­ла­ли со вхо­дом.

Этот ввод час­то име­ну­ют не За­пуск [Enable], а Так­ты [Clock], и да­лее бу­дет по­нят­но, по­че­му. Ес­ли не об­ра­щать внимания на нижний вы­ход и сфо­ку­си­ро­вать­ся толь­ко на верхнем, вы уви­ди­те, что у нас по­лу­чи­лась 1-бит­ная ячей­ка па­мя­ти. Вы пи­ше­те 1 или 0, ак­ти­ви­руя так­то­вый сиг­нал, и за­тем за­дае­те вход, что­бы он имел лю­бую ве­ли­чи­ну, ко­то­рую вы хо­ти­те со­хранить. Когда вы от­клю­чи­те так­ты, ячей­ка со­хранит эту ве­ли­чи­ну, и вы смо­же­те счи­тать ее с верхнего вы­хо­да.

На­чи­ная с на­стоя­ще­го мо­мен­та и да­лее, когда нам по­на­до­бят­ся триг­ге­ры за­держ­ки, мы бу­дем ис­поль­зо­вать встро­ен­ное пред­став­ление KTechLab. Вы най­де­те его в раз­де­ле Integrated Circuits, где оно име­ну­ет­ся “D Flip-Flop [муль­ти­виб­ра­тор]”. Они поч­ти иден­тич­ны, но все же обя­за­тель­но про­чи­тай­те раз­дел За­пускаю­щий пе­ре­пад сиг­на­ла, что­бы по­нять разницу ме­ж­ду триг­ге­ром за­держ­ки и D Flip-Flop.

При­меним ре­зуль­та­ты

Мы мо­жем лег­ко ин­тег­ри­ро­вать эту па­мять в соз­дан­ное на­ми АЛУ, за­менив ин­ди­ка­то­ры вы­во­да триг­ге­ра­ми за­держ­ки – по од­но­му на ка­ж­дый пе­ре­клю­ча­тель. Тогда па­мять смо­жет за­помнить ре­зуль­та­ты по­следнего вы­чис­ления. Вро­де и не слиш­ком по­лез­но, но за­то да­ет воз­мож­ность со­единить раз­ные опе­ра­ции в на­шем АЛУ, ис­поль­зуя т. н. ре­гистр-сумматор.

(thumbnail)
> Рис. 4. Триг­гер D-ти­па ра­бо­та­ет как 1-бит­ная схе­ма па­мя­ти, ре­шая про­бле­мы с за­кры­тым фик­са­то­ром SR. Под ос­нов­ной схе­мой рас­по­ла­га­ет­ся пре­зен­та­ция встро­ен­но­го D FlipFLop от KTechlab.

Что­бы это сде­лать, при­дет­ся под­верг­нуть ранее соз­дан­ное на­ми АЛУ неко­то­ро­му ре­ди­зай­ну. Его ре­зуль­та­ты по­ка­за­ны на рис. 5. Прин­ци­пы поч­ти те же: ме­ж­ду вхо­да­ми и вы­хо­да­ми по-прежнему су­ще­ст­ву­ют муль­ти­п­лек­со­ры, что­бы осу­ще­ст­в­лять вы­бор ме­ж­ду раз­ны­ми опе­ра­ция­ми, и име­ет­ся на­бор ко­дов опе­ра­ций на вы­бор. Од­на­ко мно­гое и из­менилось. Са­мое важ­ное, что мы ин­тег­ри­ро­ва­ли триг­ге­ры за­держ­ки в вы­вод, что­бы схе­ма мог­ла за­помнить пре­ды­ду­щий ре­зуль­тат. Это и есть ре­гистр.

Суть ре­ги­ст­ра в том, что это как бы элек­трон­ный блок­нот – ме­сто, ку­да за­но­сят­ся чис­ла для дальней­шей об­ра­бот­ки. Это уско­ря­ет слож­ные опе­ра­ции, по­сколь­ку из­бав­ля­ет про­цес­сор от необ­хо­ди­мо­сти за­пи­сы­вать про­ме­жу­точ­ные ре­зуль­та­ты в па­мять и за­тем за­гру­жать их сно­ва, что­бы вы­полнить опе­ра­цию.

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

(thumbnail)
Рис. 5. На­ше за­кон­чен­ное АЛУ де­мон­ст­ри­ру­ет, как мож­но ин­тег­ри­ро­вать схе­му па­мя­ти в про­цес­сор.

Но­вые ко­ман­ды

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

Так­же име­ет­ся ко­ман­да LOD, или load, ко­то­рая вво­дит пер­вое чис­ло для об­ра­бот­ки в ре­гистр. И. на­конец, вы так­же мо­же­те об­ра­тить внимание, что мы со­единили Rst на ка­ж­дом триг­ге­ре с пе­ре­клю­ча­те­лем, что­бы мож­но бы­ло пе­ре­клю­чить со­стояние ре­ги­ст­ра в пол­ный 0 одним на­жа­ти­ем.

Пе­ре­клю­ча­тель clock яв­ля­ет­ся эк­ви­ва­лен­том enable в ранее соз­дан­ном на­ми триг­ге­ре за­держ­ки, но обя­за­тель­но про­чти­те врез­ку «Сра­ба­ты­вание по пе­ре­па­ду сиг­на­ла», что­бы по­нять, как это ра­бо­та­ет.

Соз­дав та­кую схе­му, вы мо­же­те за­гру­зить чис­ло в ре­гистр, вве­сти дру­гое чис­ло на осталь­ные вход­ные пе­ре­клю­ча­те­ли, вы­брать опе­ра­цию пе­ре­клю­ча­те­ля­ми opcode, а за­тем вклю­чить пе­ре­клю­ча­тель clock и вы­полнить опе­ра­цию над со­дер­жи­мым ре­ги­ст­ра – ес­ли это ко­ман­да ADD, то и над со­дер­жи­мым пе­ре­клю­ча­те­лей вво­да (опе­ра­ции LSL и LSR об­ра­ба­ты­ва­ют толь­ко со­дер­жи­мое ре­ги­ст­ра). Ес­ли вы за­тем за­хо­ти­те вы­полнить дру­гую опе­ра­цию с ре­зуль­та­том, то и такое бу­дет воз­мож­но.

Вот и все, что мы со­би­ра­лись рас­смот­реть в ста­тье это­го ме­ся­ца. Те­перь у вас долж­но быть встро­ен­ное АЛУ с воз­мож­но­стью вы­полнения раз­ных опе­ра­ций, как оп­ре­де­ле­но в спе­ци­аль­ных opcodes, спо­соб­ных со­единить эти опе­ра­ции бла­го­да­ря вво­ду ре­ги­ст­ра.

Нам пред­сто­ит еще дол­гий путь. В сле­дую­щем вы­пуске мы рас­смот­рим, как ин­тег­ри­ро­вать в про­цес­сор ОЗУ, со­единив его с уст­рой­ст­вом управ­ления, и вве­сти в ОЗУ про­грам­му, что­бы про­цес­сор вы­полнил всю се­рию команд. |


Персональные инструменты
купить
подписаться
Яндекс.Метрика