11.シリアルパラレル変換回路を作る
ここでは、シリアルで入力される8bitをパラレルデータとして出力する回路を作成します。
問題
外部からシリアルで8bitのデータが入力されると(ENがHレベル1クロックで1bit入力)、パラレル8bit出力(出力中はvalid信号をHにし、出力期間外はLにする)する回路を作りなさい。
#出力の上位bitには、一番早くシリアル入力したデータが入ります
さてどう作る?
さあどうしましょうか。まず方針を立ててみましょう。
・シリアル受信回路(D型フリップフロップを並べる)が必要
・パラレル出力する際、相手に取得してもらうタイミングvalidをカウンタで作る
・パラレル出力は、D型フリップフロップ群の各出力をつなげればよい
まずはこんな感じでイメージできれば良いと思います。(頭でイメージしたものは紙に書いておくと良いと思います)
VHDLで書いてみる
上部のprocess(clk, reset_n)では、シリアルデータを受けて記憶するためD型フリップフロップ8個を直列に並べています。入力enがHの時にこのフリップフロップ群(シフトレジスタとも言う)は動作します。
下部のprocess(clk, reset_n)では、出力のvalid信号(出力データが確定したことを示す信号)がHになるタイミングを作る回路になります。入力enの数を数えて8個入力されたら(=8ビット入力されたら)0に戻しています。
それでその下段にある、valid <= icnt(3); はすなわちカウンタ4ビット目がvalid信号だと定義していますが、8個(2進数”1000”)数えたら0に戻るカウンタになっていますから実質8個データが入力されるごとにvalidが1クロック分Hになる設計になっています。
最下段にある 出力dout <= id7&id6&id5&id4&id3&id2&id1&id0; はフリップフロップ群に入っている出力データを単純に8ビット化しています。要するに8個データが入力されるとデータが揃うということになります。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity sp8 is
Port ( clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
en : in STD_LOGIC;
d : in STD_LOGIC;
valid : out STD_LOGIC;
dout : out STD_LOGIC_VECTOR (7 downto 0));
end sp8;
architecture rtl of sp8 is
signal id0,id1,id2,id3,id4,id5,id6,id7 : STD_LOGIC;
signal icnt: STD_LOGIC_VECTOR (3 downto 0);
begin
process(clk, reset_n)
begin
if reset_n = '0' then
id0 <= '0';
id1 <= '0';
id2 <= '0';
id3 <= '0';
id4 <= '0';
id5 <= '0';
id6 <= '0';
id7 <= '0';
elsif rising_edge(clk) then
if en = '1' then
id0 <= d;
id1 <= id0;
id2 <= id1;
id3 <= id2;
id4 <= id3;
id5 <= id4;
id6 <= id5;
id7 <= id6;
end if;
end if;
end process;
process(clk, reset_n)
begin
if reset_n = '0' then
icnt <= "0000";
elsif rising_edge(clk) then
if icnt = "1000" then
icnt <= "0000";
elsif en = '1' then
icnt <= icnt + '1';
end if;
end if;
end process;
valid <= icnt(3);
dout <= id7&id6&id5&id4&id3&id2&id1&id0;
end rtl;
Verilogで書いてみる
VHDLコードをChatGPTでVerilog変換してみました。ざっと見たところ問題なさそうですね。忠実にVHDLから変換できていると思います。
module sp8 (
input wire clk, // クロック入力
input wire reset_n, // アクティブローのリセット入力
input wire en, // イネーブル入力
input wire d, // データ入力
output wire valid, // 有効フラグ出力
output wire [7:0] dout // データ出力
);
reg id0, id1, id2, id3, id4, id5, id6, id7; // シフトレジスタ用レジスタ
reg [3:0] icnt; // カウンタ用レジスタ
// シフトレジスタ処理
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
id0 <= 1'b0;
id1 <= 1'b0;
id2 <= 1'b0;
id3 <= 1'b0;
id4 <= 1'b0;
id5 <= 1'b0;
id6 <= 1'b0;
id7 <= 1'b0;
end else if (en) begin
id0 <= d;
id1 <= id0;
id2 <= id1;
id3 <= id2;
id4 <= id3;
id5 <= id4;
id6 <= id5;
id7 <= id6;
end
end
// カウンタ処理
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
icnt <= 4'b0000;
end else if (icnt == 4'b1000) begin
icnt <= 4'b0000;
end else if (en) begin
icnt <= icnt + 1;
end
end
// valid信号の生成
assign valid = icnt[3];
// dout信号の生成
assign dout = {id7, id6, id5, id4, id3, id2, id1, id0};
endmodule
シミュレーション結果
実は動作イメージ図がシミュレーション結果になっていますが、再度掲載します。入力enが8個入るごとに出力データが確定してそのタイミングで出力validが1クロック分Hが出ていることが分かると思います。
おわりに
今回はシリアルパラレル変換回路をご紹介しました。コードを書きながら考えると難しいので、書く前にある程度の設計方針を立てることをおすすめします。できればこの時点で動作イメージ図を手書きでノートに書き留めておければベストでしょう。ありがとうございました。