Verilogでアナログシュミレート?
状態方程式の一般形式は
です。制御系の技術者にはおなじみの式ですね。これを4次のルンゲクッタで計算させています。
呼び出し形式は
| $runge_kutta("ファイル名”、モード、Input_RealArray、Output_RealArray); |
それでは、使い方のデモソースです。
Initialの$runge_kuttaで、遷移行列ファイルの指定、モードの指定、入出力のREALアレーを定義します。
次にAlways文で、毎クロック呼び出します。
`timescale 1ns / 1ns
//Apr.18.2005 rungekutta IF changed.
module runge_kutta_test;
parameter freq =900e3;
parameter dac_width=10;
real read_array[0:10];
real write_array[0:10];
real read_array2[14:24];
real write_array2[10:24];
real t,amp,ar;
reg clock=0;
reg [15:0] filt_out1,filt_out2;
reg [dac_width-1:0] dac_input;
real range;
reg Reset;
wire dac_out;
real ar;
always #1 clock=~clock;
always @(posedge clock) begin
t=$time()*1e-9;
amp=($sin(2*$M_PI()*t*freq)+1)*range;
dac_input=$rtoi(amp);
read_array[0]=dac_out*6283185.3;//cut off 1e6Hz degree2
//ar=$sin(2*$M_PI()*t*freq);
read_array2[14]=dac_out*6283185.3;//cur off 1e6Hz degree10
$runge_kutta("Ae6_2.mat",1);
$runge_kutta("Ae6.mat",1);
ar=dac_out*6283185.3;
// filt_out1=write_array[1]*range;//2degree 0+(2-1)=1
// filt_out2=write_array2[19]*range;//10degree 10+(10-1)=19
filt_out1=write_array[0]*range;//Apr.18.2005 2degree 0+(2-1)=1
filt_out2=write_array2[10]*range;//Apr.18.2005 10degree 10+(10-1)=19
//$display("filt_out2=%e %e",ar,write_array2[11]);
end
sigma_delta #(dac_width) dac1(dac_out, dac_input,clock, Reset);
initial begin
Reset=1;
range=((1 << dac_width)-2)/2;
$runge_kutta("Ae6_2.mat",0,read_array,write_array);
$runge_kutta("Ae6.mat",0,read_array2,write_array2);
#100 Reset=0;
#100000 $finish;
end
endmodule
module sigma_delta(out, DACin, Clk, Reset);
parameter dac_with=10;
input [dac_with-1:0] DACin; //
input Clk;
input Reset;
output out;
reg [dac_with-1+2:0] R1;
wire out=R1[dac_with-1+2];
wire [dac_with-1+2:0] delta= {R1[dac_with-1+2], R1[dac_with-1+2], {dac_with{1'b0}}};
wire [dac_with-1+2:0] adder1 = {2'b00,DACin} + delta;
wire [dac_with-1+2:0] adder2 = adder1 + R1;
always @(posedge Clk or posedge Reset)
begin
if(Reset) R1 <= {1'b1, 11'h000};
else R1 <=adder2;
end
endmodule
|
本例では、一次のシグマデルタDAコンバータでDA変換しています。シグマデルタコンバータの出力は1ビットのビットストリームになります。これを2次、および10次のアナログバターワースLPFでアナログに戻してみます。

10次フィルタの行列は次で与えられます。
ファイルとして読み込むのは、Aマトリクスだけです。残りは、Verilogソース内でプログラムします。ルンゲクッタの計算結果xはwrite_array2に入ります。write_array2は、LEFT_ADDRESS10から始まり10次の行列ですので出力は19にはいることになります。
A =
1.0e+008 *
-1.2412 -0.6283 0 0 0 0 0 0 0 0
0.6283 0 0 0 0 0 0 0 0 0
0 0.6283 -1.1197 -0.6283 0 0 0 0 0 0
0 0 0.6283 0 0 0 0 0 0 0
0 0 0 0.6283 -0.8886 -0.6283 0 0 0 0
0 0 0 0 0.6283 0 0 0 0 0
0 0 0 0 0 0.6283 -0.5705 -0.6283 0 0
0 0 0 0 0 0 0.6283 0 0 0
0 0 0 0 0 0 0 0.6283 -0.1966 -0.6283
0 0 0 0 0 0 0 0 0.6283 0
B =
1.0e+007 *
6.2832
0
0
0
0
0
0
0
0
0
C =
0 0 0 0 0 0 0 0 0 1.0000
D =
0
下がシミュレーション波形です。DACの1ビット出力DAC_OUTがPPM変調されている様子がわかります。これをフィルタに通すと、filt_out1,filt_out2になります。2次と10次の違いはあまりわかりませんが、とりあえず、10次の方が次数が大きい分位相Delayが大きいことがわかります。

最後は、VPI ソースリストです。
// Mar.19.2004 www.sugawara-systems.com
// Linear System Simulator on Verilog2001 VPI
// Apr.18.2005 Bug fix address to only 1 output, change output port0
//
#include "runge_kutta.h"
#include "math.h"
# include "..\..\vpi\vpi\vpi_user.h"
#include <cassert>
#include <map>
#include <string>
extern int reset_lexor(const char * path);
extern bool parse_mat_data();
extern vector< vector<double*> *> *doubles_types;
//static runge_kutta *gloval_ptr=0;
static map <string , runge_kutta*> file_map;
runge_kutta::runge_kutta()
{
doubles_list=0;
continue_runge_kutta=false;
old_time=0;
zh=0;
mode=INITIAL;
}
runge_kutta::~runge_kutta()
{
}
bool runge_kutta::file_read(string file_name)
{
bool success=reset_lexor(file_name.c_str());
if(success){
success=parse_mat_data();
if (success) doubles_list=doubles_types;
else {
vpi_mcd_printf(1,"$runge_kutta: can not read coefficient file.\n");
}
}else {
vpi_mcd_printf(1,"$runge_kutta:can not open coefficient file.\n");
}
return success;
}
bool runge_kutta::check_and_dump_double_list()
{
vpi_mcd_printf(1,"coefficient file has read.\n");
rows=doubles_list->size();
assert(rows);
columns= (*doubles_list)[0]->size();
bool error=false;
if (rows ==columns){
vpi_mcd_printf(1,"Matrix is [%d][%d]\n",rows,columns);
for (unsigned i=0; i< rows;i++) {
if ((*doubles_list)[i]->size() !=columns) error=true;
vector <double> a;
for (unsigned m=0; m < (*doubles_list)[i]->size();m++){
double temp=*(*(*doubles_list)[i])[m];
a.push_back(temp);
vpi_mcd_printf(1,"A Matrix[%d][%d]=%f \n",i,m,temp);
}
A.push_back(a);
}
if (columns !=rows) error =true;
if (!error) {
//Initialize array
for (unsigned i=0; i<rows;i++){
zx.push_back(0.0);
zdx.push_back(0.0);
zq.push_back(0.0);
}
for (unsigned i=0; i< rows;i++) {
if (i==0) B.push_back(1.0);//Jan.5.2005
else B.push_back(0.0);//Jan.5.2005
if (i==rows-1) C.push_back(1.0);//Jan.5.2005
else C.push_back(0.0);//Jan.5.2005
}
D=0.0;//Jan.5.2005
}else {
vpi_mcd_printf(1,"$runge_kutta:can not read coefficient file.\n");
}
return !error;
}else {
// x=A nxn B nx1
// y=C 1xn D 1x1
// n+n+1+1
if (rows ==2*columns+2){
//vpi_mcd_printf(1,"Matrix is/*行列は*/[%d][%d]./*行列です。*/\n",rows,columns);
for (unsigned i=0; i< columns;i++) {
if ((*doubles_list)[i]->size() !=columns) error=true;
vector <double> a;
for (unsigned m=0; m < columns;m++){
double temp=*(*(*doubles_list)[i])[m];
a.push_back(temp);
vpi_mcd_printf(1,"A Matrix[%d][%d]=%f \n",i,m,temp);
}
A.push_back(a);
}
//B
for (unsigned i=0; i< columns;i++) {
double temp=*(*(*doubles_list)[i+columns])[0];
vpi_mcd_printf(1,"B Matrix[%d][0]=%f \n",i,temp);
B.push_back(temp);
}
//C
for (unsigned i=0; i< columns;i++) {
double temp=*(*(*doubles_list)[2*columns])[i];
vpi_mcd_printf(1,"C Matrix[0][%d]=%f \n",i,temp);
C.push_back(temp);
}
//D
D=*(*(*doubles_list)[2*columns+1])[0];
vpi_mcd_printf(1,"D Matrix[0][0]=%f \n",D);
if (!error) {
//Initialize array
for (unsigned i=0; i< columns;i++){
zx.push_back(0.0);
zdx.push_back(0.0);
zq.push_back(0.0);
}
}else {
vpi_mcd_printf(1,"$runge_kutta:can not read coefficient.\n");
}
return !error;
}else {
vpi_mcd_printf(1,"$runge_kutta:format of coefficient file should be (row ==2*coulumn+2).\n");
}
}
}
static vpiHandle get_module_handle(vpiHandle call_handle)
{
vpiHandle obj=call_handle;
while (vpi_get(vpiType, obj) != vpiModule) {
obj = vpi_handle(vpiScope, obj);
}
return obj;
}
void runge_kutta::get_delta_time(vpiHandle call_handle)
{
s_vpi_time now;
now.type=vpiScaledRealTime;
vpi_get_time(0, &now);
double current_time=now.real;
now.type = vpiSimTime;
vpi_get_time(0, &now);
int units = vpi_get(vpiTimeUnit,get_module_handle(call_handle));
if (old_time !=current_time){
double scale=pow((double)10,(double)units);//キャストがないと駄目
zh=(current_time-old_time)*scale;//
old_time=current_time;
}
}
void runge_kutta::do_integ()
{
double dk,dy;
vector <double > inputs;
s_vpi_value value;
value.format=vpiRealVal;
// unsigned no_of_int=rows;
unsigned no_of_int=columns;//Dec.20.2004;
//現在のVerilogのインプットアレーを読み出す。
for (unsigned i=0; i< no_of_int;i++){//Read Current Input Array from Verilog
vpiHandle word_index = vpi_handle_by_index(m_read_item,read_mem_left_address+i);
vpi_get_value(word_index, &value);
double temp=value.value.real;
inputs.push_back(temp);
}
if (zh==0.0) return;//zhが未設定ならなにもしない。
//Matrix 乗算
for (unsigned mmode=1; mmode <=4;mmode++){
for (unsigned ix=0;ix< no_of_int;ix++){//行
double sum=0;
for (unsigned iy=0; iy< no_of_int;iy++) {//列
//double coeff=*(*(*doubles_list)[ix])[iy];
double coeff=A[ix][iy];//Dec.20.2004
sum +=zx[iy]*coeff;
}
double temp=inputs[ix];
//zdx[ix]=sum+inputs[ix];//行
zdx[ix]=sum+B[ix]*inputs[ix];//Dec.20.2004
}
//積分処理
for (unsigned m=0;m<no_of_int ;m++ ){
switch (mmode) {
case 1 :
dk=zdx[m]*zh;
dy=0.5*(dk-2.0*zq[m]);
zx[m]=zx[m]+dy;
zq[m]=zq[m]+3.0*dy-0.5*dk;
break;
case 2 :
dk=zh*zdx[m];
dy=cc1*(dk-zq[m]);
zx[m]=zx[m]+dy;
zq[m]=zq[m]+3.0*dy-cc1*dk;
break;
case 3 :
dk=zh*zdx[m];
dy=cc2*(dk-zq[m]);
zx[m]=zx[m]+dy;
zq[m]=zq[m]+3.0*dy-cc2*dk;
break;
case 4 :
dk=zdx[m]*zh;
dy=(dk-2.0*zq[m])/6.0;
zx[m]=zx[m]+dy;
zq[m]=zq[m]+3.0*dy-dk/2.0;
break;
} /* endswitch */
}//end for
}//end for
//結果をVerilogのアレーに書き込む
//Apr.18.2005
vpiHandle word_index = vpi_handle_by_index(m_write_item,write_mem_left_address+0);//Apr.18.2005 no_of_int-1);
//Write the result to Write Array in Verilog
double temp=0;
for (unsigned i=0; i< no_of_int;i++){//Read Current Input Array from Verilog
//double temp=zx[i];
temp +=zx[i]*C[i]+inputs[i]*D;//Dec.20.2004
}
value.value.real=temp;
vpi_put_value(word_index, &value,0, vpiNoDelay);//Apr.18.2005
/*
for (unsigned i=0; i< no_of_int;i++){//Read Current Input Array from Verilog
vpiHandle word_index = vpi_handle_by_index(m_write_item,write_mem_left_address+i);
//double temp=zx[i];
double temp=zx[i]*C[i]+inputs[i]*D;//Dec.20.2004
value.value.real=temp;
vpi_put_value(word_index, &value,0, vpiNoDelay);
}
*/
}
//Verilog IFに接続されているアレー、その他のチェック
//一度だけ読み出しアレーについては、ハンドル情報をSaveしておく
bool runge_kutta::get_property_on_verilog_if()
{
s_vpi_value value;
vpiHandle left_range;
vpiHandle right_range;
if (m_read_item == 0 || m_write_item==0) {
vpi_mcd_printf(1,"$runge_kutta: invalid parameter.\n");
return false;
}
if (vpi_get(vpiType, m_read_item) != vpiMemory) {
vpi_mcd_printf(1,"$runge_kutta:3rd parameter should be real array.\n");
return false;
}
if (vpi_get(vpiType, m_write_item) != vpiMemory) {
vpi_mcd_printf(1,"$runge_kutta:4th parameter should be real array.\n");
return false;
}
value.format = vpiIntVal;
vpi_get_value(mode_item, &value);
if (value.value.integer !=(int)(INITIAL)){
vpi_mcd_printf(1,"$runge_kutta:First call should be mode0.\n");
return false;
}
//Read Memory Input for runge_kutta
left_range = vpi_handle(vpiLeftRange, m_read_item);
value.format = vpiIntVal;
vpi_get_value(left_range, &value);
read_mem_left_address = value.value.integer;
right_range = vpi_handle(vpiRightRange, m_read_item);
value.format = vpiIntVal;
vpi_get_value(right_range, &value);
read_mem_right_address = value.value.integer;
if (read_mem_left_address > read_mem_right_address){
vpi_mcd_printf(1,"$runge_kutta: memory range must be Left<Right\n");
return false;
}
if ((read_mem_right_address-read_mem_left_address+1) < columns){//Dec.10.2004
vpi_mcd_printf(1,"$runge_kutta: invalid memory range.\n");
return false;
}
//Write Memory Output for runge_kutta
left_range = vpi_handle(vpiLeftRange, m_write_item);
value.format = vpiIntVal;
vpi_get_value(left_range, &value);
write_mem_left_address = value.value.integer;
right_range = vpi_handle(vpiRightRange, m_write_item);
value.format = vpiIntVal;
vpi_get_value(right_range, &value);
write_mem_right_address = value.value.integer;
if (write_mem_left_address > write_mem_right_address){
vpi_mcd_printf(1,"$runge_kutta:memory range should be LEFT<RIGHT.\n");
return false;
}
if ((write_mem_right_address-write_mem_left_address+1) < columns){//Dec.20.2004
vpi_mcd_printf(1,"$runge_kutta:invalid memory range.\n");
return false;
}
vpiHandle words = vpi_iterate(vpiMemoryWord, m_read_item);
vpiHandle item = vpi_scan(words);
unsigned wwid = vpi_get(vpiSize, item);
if (wwid !=64){
vpi_mcd_printf(1,"$runge_kutta:memory should be real type of array.\n");
return false;
}
words = vpi_iterate(vpiMemoryWord, m_write_item);
item = vpi_scan(words);
wwid = vpi_get(vpiSize, item);
if (wwid !=64){
vpi_mcd_printf(1,"$runge_kutta:memory should be real type of array.\n");
return false;
}
//Finally,Initial Check all done.
return true;
}
//毎クロック呼び出される。
static int sys_runge_kutta_calltf(char*name)
{
runge_kutta* local_ptr=0;
s_vpi_value value;
vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
vpiHandle argv = vpi_iterate(vpiArgument, sys);
vpiHandle file_handle=vpi_scan(argv);//First Parameter
//First parameter
if (!file_handle) {
vpi_mcd_printf(1,"$runge_kutta:missing parameter.\n");
//Mar.25.2004 vpi_free_object(argv);
return 0;
}
//ファイル名取得チェック
if (vpi_get(vpiType, file_handle)!=vpiConstant &&
vpi_get(vpiType, file_handle)!=vpiParameter){
vpi_mcd_printf(1,"$runge_kutta:file name should be string type.\n");
vpi_free_object(argv);
return 0;
}
if (vpi_get(vpiConstType,file_handle) != vpiStringConst) {
vpi_mcd_printf(1,"$runge_kutta:file name should be string type.\n");
vpi_free_object(argv);
return 0;
}
//ファイル名取得
value.format = vpiStringVal;
vpi_get_value(file_handle, &value);
string file_name=value.value.str;
//2nd parameter
vpiHandle mode_item = vpi_scan(argv);
if (mode_item ==0){
vpi_mcd_printf(1,"$runge_kutta:missing mode parameter.\n");
//Mar.25.2004vpi_free_object(argv);
return 0;
}
map<string ,runge_kutta* > ::const_iterator im=file_map.find(file_name);
if (im ==file_map.end()){//新しいファイル処理
local_ptr=new runge_kutta();
file_map[file_name]=local_ptr;
bool success=local_ptr->file_read(file_name);
if (success) {
bool success=local_ptr->check_and_dump_double_list();
if (success){
//3rd/4th parameter
local_ptr->m_read_item=vpi_scan(argv);//READ ARRAY
if(! local_ptr->m_read_item) return 0;//argv はdelete は、Verilogでデリート済み
local_ptr->m_write_item=vpi_scan(argv);//WRITE ARRAY
if(! local_ptr->m_write_item) return 0;//argv はdelete は、Verilogでデリート済み
local_ptr->mode_item=mode_item;
if(local_ptr->get_property_on_verilog_if()) local_ptr->go_on();
}
}
}else {//既にMapに登録されている。
local_ptr=file_map[file_name];
}
if(local_ptr->go_()){//初期化でエラーなかったらModeパラメータで制御
local_ptr->get_delta_time(sys);
s_vpi_value value;
value.format = vpiIntVal;//モードパラメータを取得
vpi_get_value(mode_item, &value);
unsigned mode=value.value.integer;
if (mode==0) ;
else if(mode==1) local_ptr->do_integ();
}
vpi_free_object(argv);
return 0;
}
//ユーザシステムタスクを登録
extern void sys_ruge_kutta_register()
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$runge_kutta";
tf_data.calltf = sys_runge_kutta_calltf;
tf_data.compiletf = 0;
tf_data.sizetf = 0;
tf_data.user_data = 0;
vpi_register_systf(&tf_data);
}
|