Tag Archives: verilogHDL

PLI에서 TCP/IP를 통해서 통신하고, perl server에서 처리하기

예전에 PLI에서 윈도우 제어 하려고 별짓을 다했었는데, 그 중에 PLI에서 TK 윈도우를 바로 부른 것도 있었습니다. PLI에서 TK를 부르는(C-TK interwork을 이용한) 방법은 TK 스크립을 거의 직접 쓸 수 있다는 점에서 편리하긴 한데, NCVerilog에서 너무 버전을 심하게 탄다는 단점(TK의 버전도 맞춰 줘야 합니다. -_-;)이 있어서 환경이 바뀌면서 잘 안쓰게 되더군요.

게다가 시뮬레이션 돌리면서 이런 저런것을 실시간 출력할때 UNIX/Linux/Windows 안가리는 인터페이스를 고민하다보니, TCP/IP와 Perl 만한게 없더군요. Simulator에서 이런 저런 GUI 부분이 귀찮아서 socket 기반으로 만들었던 기억을 되살려 하나 만들어봤습니다. PLI에서 TCP/IP 패킷을 전송하는 부분을 하나 만들어봤습니다.

#include 
#include <"arpa/inet.h">
#include <"net/netinet.h">
#include <"sys/socket.h">
#include <"sys/types.h"> #include "vpi_user.h"

#define DEST_IP "127.0.0.1"
#define DEST_PORT 7890

static struct sockaddr_in dest_addr;

static int count = 0;
static int sockfd;
int counta;

PLI_INT32 cosim_hello_calltf(PLI_BYTE8 *userdata) {
        char buf[1024];
        s_vpi_time ctime;
        ctime.type = vpiScaledRealTime;
        vpi_get_time(NULL, &ctime);
        vpi_printf("[%2.2f] Hello VPI: %d, %d\n", ctime.real, count, counta);
        sprintf(buf, "\n##[%2.2f] Hello VPI: %d, %d\n", ctime.real, count, counta);
        send(sockfd, &buf, sizeof(buf), 0);

        count++;
        counta++;
        return(0);
}

PLI_INT32 cosim_hello_init() {
        int len, result;
        counta = 100;
        sockfd = socket(AF_INET, SOCK_STREAM, 0);

        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(DEST_PORT);
        dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);

        len = sizeof(dest_addr);
        result = connect(sockfd, (struct sockaddr *)&dest_addr, len);

        if (result == -1) {
                fprintf(stderr, "socket open error\n");
                exit(1);
        }

        return(0);
}

void cosim_register_hello() {
        s_vpi_systf_data tf_data;
        tf_data.type = vpiSysTask; // make as task
        tf_data.sysfunctype = 0;
        tf_data.tfname = "$cosim_hello";
        tf_data.calltf = cosim_hello_calltf;
        tf_data.compiletf = cosim_hello_init;
        tf_data.sizetf = NULL;
        tf_data.user_data = NULL;
        vpi_register_systf(&tf_data);
}

void (*vlog_startup_routines[])() = {cosim_register_hello, 0}


별 내용은 없고, 그냥 verilog code에서 cosim_hello()를 호출하면 loop돌면서 값을 출력하는 예제입니다.

 

이 코드 틀은 다이나릿의 기안도 박사님 IDEC 강좌 자료에 있는 “HW/SW 동시 협조 시뮬레이션”이란 강좌의 첫번째 PLI 예제에서 따왔으며, 저는 이 함수에 TCP/IP 전송이 가능하도록 수정하였습니다.

컴파일은 다음과 같이 일반적인 NCVerilog 컴파일과 다르지 않지요. (Windows에서 Modelsim 사용하시는 분은 gcc보다는 visual c++의 cl 을 사용하시는 것이 속 편합니다. MingW 버전의 gcc가 되기는 하는데, Modelsim에서 버전을 상당히 심하게 탔던 것으로 기억됩니다. 요즘 버전은 어떨지 모르겠습니다만. )

 

$gcc -I$CDS_HOME/tools/inca/include -c cosim_hello.c
$ld -shared -o cosim.so cosim_hello.o

NCVerilog Compile & Elaboration

 

$ ncvlog -work worklib test_hello.v
$ ncelab worklib.test_hello -loadvpi ./cosim:cosim_register_hello
$ ncsim worklib.test_hello


이런 식으로 사용하면 되는데, 위의 패킷을 받아줄 서버는 간단히 perl로 짜주면 됩니다. Perl-TK로 GUI를 작성하는 것도 가능하구요.

이때 한가지 주의해야 할 점은 perl의 IO::INET 의 accept() 함수가 blocking type이기 때문에 이것을 non-blocking type으로 해 주시고 loop을 돌려야지만 DoOneEvent() 함수가 정상적으로 수행된다는 점이지요.
(설마 MainLoop()로 돌리실 분은 없을 테니 ^^;) 저도 처음엔 DoOneEvent함수가 좀처럼 동작을 안하는 것처럼 느껴져서 헤맸습니다. 결론은 accept()함수 문제더군요.

perl 함수의 주요 부분만간단히 정리하자면(예제를 위해서 perl script를 하나 더 만들기가 귀찮고, 전체 내용은 회사 작업이라 공개할 수 없고.. 라는 어려움 때문에 별로 문제 안되는 부분만 올립니다. )



use strict;
use IO::Socket;
use Tk;
use encoding 'utf-8';    # 한글 쓰시려면..
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);

my $portnum = 7890;

# make a socket

my $mw = MainWindow->new(-title=>"Terminal");

.....

my $sock = IO::Socket::INET->new(
                LocalHost => 'localhost',
                LocalPort => $portnum,
                Proto => 'tcp',
                Listen => 10, # SOMAXCONN 으로 해도 됩니다.
                Reuse => 1,
                TimeOut => 1,
                );

....
my($new_sock, $c_addr, $buf, $flag);
...

# NonBlocking으로 만듭시다.
$flag = fcntl($sock, F_GETFL, 0) or die "Can not get flag: $!\n";
$flag = fcntl($sock, F_SETFL, $flag | O_NONBLOCK) or die "Can not set flag: $!\n";

while(1) {
        while (($new_sock, $c_addr) = $sock->accept()) {
                ... 소켓에서 읽어서 일하세요....
                        DoOneEvent(0);
        }

 

요런 식이라는 것이지요. 중간 중간에 엄청 생략되어 있음은 유의하세요..

verilog-mode: verilog coder를 위한 Emacs의 친구..

태초에 두가지 에디터를 종교로 삼는 집단이 있었으니, 한 부류는 vi를 숭배했으며 또한 부류는 emacs를 숭배하였다.

종종 신앙으로까지 불리울 정도로 [[Emacs]]나 [[vi]]는 폭넓은 사용자 층을 지니고 있습니다. 물론, 국내에서는 unix/linux환경보다는 압도적인 PC환경이고, 설계도 왠만하면 PC에서 수행하려는 경향이 강한지라.. [[ultraedit]]나 [[acroedit]]를 좋아하시는 분들이 더 많습니다.

물론, 한번 빠져들면 종교가 될 정도로 강력한 vim+ctags 의 조합은 대부분의 PC용 에디터보다 훨씬 좋았습니다. 그러한 종교를 개종 시킬만큼 강력한 강적이 나타났으니 바로 [[verilog-mode]] 입니다.
최근 [[linux]]의 emacs에는 대부분 verilog-mode가 같이 깔려 있는데, 특히 top module에서 하위 모듈을 instantiation할때 엄청나게 편합니다!!!!!

간략하게 설명드리자면, [[verilog]] mode는 emacs에서 사용되는 verilog 입력 모드인데, AUTO라는 강력한 기능이 있습니다. 예를 들어, /*AUTOARG*/같은 경우

module aaa(/*AUTOARG*/);

와 같이 적어두고, 필요한 입출력들을 기술하면 module의 argument는 자동적으로 생성됩니다.

가장 강력한 기능이라 생각하는 부분은 /*AUTOINST*/기능인데, 이 기능은 만일 aaa라는 모듈을 instantiate할때

aaa U0_aaa(/*AUTOINST*/)

라고만 적어두면, 자동적으로 aaa.v를 읽고, 그 입출력을 분석해서 /*AUTOINST*/라고 되어 있는 부분에 name based instantiation을 수행합니다.

회사 후배들에게 농담으로 하는 말로, 1시간은 낑낑댈거, 10초면 합니다. 단순히 Ctrl-C, Ctrl-A를 눌러서 원하는 하위 모듈을 instance할 수 있으니까요. 그래서, 재미삼아 xemacs에서 verilog mode를 한번 쓴 이후에는 계속 emacs에 적응중입니다. (물론, vim에서 emacs batch command로 설정해서 verilog mode를 사용할 수도 있습니다. 그런데, 스타일이 별로입니다. (탭이 들어가죠..전 탭을 싫어해서.))

emacs 자체도 상당히 강력한지라 (역시 종교라니까요~), 즐겁게 작업할 수 있습니다.

개인적으로는 verilog mode의 단점으로 몇몇 환경 설정에서 단점이 보였습니다.
우선, /*AUTOARG*/ 나 /*AUTOINST*/ 에서 출력, 입력 순으로 나열되는 것입니다.
이건, 사실 예~~~전의 primitive를 만들때의 design rule이고, 요즘 RMM 룰로 보았을때는 클럭,리셋,입력,출력의 순이 되어야 정상인데 말입니다.

그래서, input/output 순서가 가능한한  RMM의 형식에 맞도록 수정된 verilog-mode를 만들어 봤습니다. Lisp를 잘 몰라서, 대충만 바꿨지만 쓰는데 문제는 없어 보입니다.

또, 국내 실정상 linter를 잘 쓰지 않기 때문에 이 부분은 nc-verilog에서 문법 체크만 하는 옵션으로, 헤더는 개인적인 license헤더로 수정되어 있습니다.  필요한 경우 그 부분만 수정하시면 되겠습니다.

verilog-mode에 유용한 자료로서, 저자가 SNUG에서 발표한 verilog-mode의 간략한 설명입니다.

이걸 보시면, verilog-mode가 어떤것인지 아주 쉽게 이해가 되실것입니다.
.. 여담이지만, 외국 SNUG에서는 이런 좋은 발표가 많은데… 유독 SUNG korea의 경우는 좀 약한 면이 없잖아 있어서 아쉽습니다.

verilog mode에 대한 자세한 사항은 http://www.verilog.com/ 혹은 http://www.veripool.com/ 에서 해당 자료를 얻을 수 있습니다.