13.レジスタを実装して和差算回路を作る
ここではCPU側に対しての一般的なインターフェースとレジスタを実装について触れていきます。
問題
以下の回路モジュール(A+B、A-B)を作成してください。
<レジスタ解説書>
アドレス 属性 解説 データ
0x00 リードライト Aの数 8bit
0x01 リードライト Bの数 8bit
0x02 リード 和の結果 8bit
0x03 リード 差の結果 8bit
#0x は16進数表記
#計算結果は負の数も扱う(ビット7は正負符号、ビット6:0はデータ)
入力信号解説
adr[1:0] 2ビットのアドレス信号
wdata[7:0] 8ビットの演算入力信号(A、B)
cs_n チップ(モジュール)セレクト信号 Lアクティブ
wr_n ライト信号 Lアクティブ
rd_n リード信号 Lアクティブ
reset_n リセット信号 非同期リセットLアクティブ
出力信号
rdata[7:0] 8ビットの演算結果出力信号
さてどう作る?
いままでと違い問題が一見複雑になったようにみえますが恐れることはありません。変わったところは入力情報がCPUインターフェースライクになったことです。このインターフェースはおおよそ以下のようになります。
ライト・・cs_nかつwr_nがLのとき指定されたadrのデータwdataをライト
リード・・cs_nかつrd_nがLのとき指定されたadrにデータrdataをリード
ということで、
・CPUインターフェースに従ってレジスタ解説書にある4つのレジスタを作る
・足し算回路を作る
・引き算回路を作る
と大別すると3つの回路を作れば良さそうです。
VHDLで書いてみる
以下の様になります。それほど難しくありません。
上から1番目と2番目の process(clk, reset_n) では、アドレス”00”と”01”にレジスタライトアクセスに対応したコードとなります。
wire10 <= reg00 + reg01;
wire11 <= reg00 - reg01; で上記レジスタに書かれた値を即時計算(和と差)しています。
with adr select と3番目のprocess(clk, reset_n)ではレジスタリードアクセスに対応したコードとなります。
最後に大事なことを言い忘れました。冒頭の
use IEEE.STD_LOGIC_SIGNED.ALL;
は負の数も扱うという宣言になります。
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_SIGNED.ALL;
entity id is
Port (
clk : in STD_LOGIC;
reset_n : in STD_LOGIC;
adr : in STD_LOGIC_VECTOR (1 downto 0);
cs_n : in STD_LOGIC;
wr_n : in STD_LOGIC;
wdata : in STD_LOGIC_VECTOR (7 downto 0);
rd_n : in STD_LOGIC;
rdata : out STD_LOGIC_VECTOR (7 downto 0)
);
end id;
architecture rtl of id is
signal reg00, reg01 : STD_LOGIC_VECTOR (7 downto 0);
signal wire10, wire11 : STD_LOGIC_VECTOR (7 downto 0);
signal rdata_int : STD_LOGIC_VECTOR (7 downto 0);
begin
process(clk, reset_n)
begin
if reset_n = '0' then
reg00 <= (others => '0');
elsif rising_edge(clk) then
if cs_n = '0' and wr_n = '0' and adr = "00" then
reg00 <= wdata;
end if;
end if;
end process;
process(clk, reset_n)
begin
if reset_n = '0' then
reg01 <= (others => '0');
elsif rising_edge(clk) then
if cs_n = '0' and wr_n = '0' and adr = "01" then
reg01 <= wdata;
end if;
end if;
end process;
wire10 <= reg00 + reg01;
wire11 <= reg00 - reg01;
-- 優先順位がないマルチプレクサ
with adr select
rdata_int <= reg00 when "00",
reg01 when "01",
wire10 when "10",
wire11 when "11",
(others => '0') when others;
process(clk, reset_n)
begin
if reset_n = '0' then
rdata <= (others => '0');
elsif rising_edge(clk) then
if cs_n = '0' and rd_n = '0' then
rdata <= rdata_int;
end if;
end if;
end process;
end rtl;
Verilogで書いてみる
VHDLコードをChatGPTでVerilog変換してみました。負の数を扱うということで
reg signed [7:0] reg00, reg01;
wire signed [7:0] wire10, wire11;
としているところがポイントでしょうか。
module id (
input wire clk,
input wire reset_n,
input wire [1:0] adr,
input wire cs_n,
input wire wr_n,
input wire [7:0] wdata,
input wire rd_n,
output reg [7:0] rdata
);
reg signed [7:0] reg00, reg01;
wire signed [7:0] wire10, wire11;
reg [7:0] rdata_int;
assign wire10 = reg00 + reg01;
assign wire11 = reg00 - reg01;
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
reg00 <= 8'b0;
end else if (!cs_n && !wr_n && adr == 2'b00) begin
reg00 <= wdata;
end
end
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
reg01 <= 8'b0;
end else if (!cs_n && !wr_n && adr == 2'b01) begin
reg01 <= wdata;
end
end
always @(*) begin
case (adr)
2'b00: rdata_int = reg00;
2'b01: rdata_int = reg01;
2'b10: rdata_int = wire10;
2'b11: rdata_int = wire11;
default: rdata_int = 8'b0;
endcase
end
always @(posedge clk or negedge reset_n) begin
if (!reset_n) begin
rdata <= 8'b0;
end else if (!cs_n && !rd_n) begin
rdata <= rdata_int;
end
end
endmodule
シミュレーション結果
実は動作イメージ図がシミュレーション結果になっていますが、再度掲載します。
左からAの値3を書き込み、Bの値1を書き込み、和の結果と差の結果をリードし、Aの値とBの値をリードし、Bの値4を書き込み、最後に差の結果をリードしています。最後のrdataが0xFFとなっていますが、これは-1を意味しています。
おわりに
今回はCPU側に対しての一般的なインターフェースとレジスタを実装し、和差算についてご紹介しました。一通り見てみるとそれほど難しくないことが体感できたかと思います。ありがとうございました。