이 프로젝트는 PIC16C57를 verilog로 기술하여 구현하는 것을 목표로, 해당 칩의 설계는 어떻게 되어있는지, 명령어 셋은 어떻게 동작하는지에 관한 지식을 필요로 한다.
한 학기, 약 3달간의 프로젝트 기간안에 완료하는 것을 목표로하고 칩 설계 및 테스트 절차는 다음과 같다.
우선 Bit-oriented file register operation, Literal and control operation에 대한 Byte Oriented Instruction ASM(Algorithm State Machine) 차트를 구상한다.
1.PIC16C57 마이크로 프로세서에서 설계한 블록을 Veriolg Module로 작성하고 통합한다.
2. 결과를 RTL Level 시뮬레이션과 Gate Level에서 시뮬레이션을 통해 확인하고 itutor 테스트를 시행한다.
3. 마지막으로 VPI(Verilog Procedural Interface)를 이용하여 12X12 BMP 파일을 입력받아 Edge Detection
결과를 각각의 시뮬레이션에서(RTL, gate level, itutor) 출력한다.
즉 pic16c57의 datasheet를 보고 동작을 모두 완벽하게 이해하여 이를 직접 verilog라는 회로설계언어로 소프트웨어 상으로 구현을 하고, bmp나 jpg 파일을 설계한 cpu의 input으로하여 image의 edge detection이 제대로 되는지를 테스트까지 하는것을 목표로 하는 프로젝트이다.
PIC16C57 칩의 block diagram이다. PIC16C57 칩은 프로그램과 데이터가 한 같은 버스내에서 접근되고 패치되는 폰 노이만 아키택처에서 발전하여, 프로그램과 데이터를 각각 다른 버스를 이용하여 접근하는 하버드 아키텍처를 채택한 칩이다. PC->ROM->IR->CU->DATAPATH에 이르는 dataflow를 가지고 있으며, PC에서 ROM으로 향하는 Address field는 11비트이고, instruction register는 12bit를 사용하기 때문에 총 2의 11승(2048k) x 12에 해당하는 메모리를 사용할 수 있다. 또한 사칙연산,시프트연산을 지원하는 ALU와 연산 결과를 저장하는 w register는 8bit를 사용하며, 레지스터 파일 내에 FSR 레지스터를 두어, 직접 주소 어드레싱과 간접 주소 어드레싱을 지원한다.
간단히 말하자면, rom 메모리에 칩이 지원하는 연산들을 모두 저장해두고, 각 사이클 마다 IR 레지스터가 명령어를 차례로 하나씩 인출해와서 해독하고 이를 중앙제어장치인 CU(contril unit)에 넘긴다.
CU를 이를 받아 다시 덧셈/뺄셈 등의 연산이면 ALU, jmp 연산이면 PC 레지스터 등 연산의 종류에 따라 다른 레지스터에 신호를 보내고, 연산의 결과를 메모리(레지스터 파일)에 저장한다.
자세한 설명은 구글에 pic16c57 datasheet라고 치면 pdf파일로 설명이 있으니 생략하고 가장 핵심인 레지스터 파일의 구조 그림만 첨부한다.
portA, B, C가 외부장치로부터 입력과 출력을 받는 레지스터이다. portA, B, C는 각각 tris A,B,C 레지스터의 제어를 받는데, 이들 레지스터는 port의 상태를 입력/출력을 바꿀수 있어 필요에 따라 입력/출력 상태를 바꿀 수 있다. 나머지 기능에 대한 설명은 데이터시트에.
이러하 기능을 바탕으로 설계한 전체 칩의 RTL 설계도는 다음과 같다.
본 프로젝트의 전체 ASM chart는 다음과 같다. asm chart는 데이터시트를 참고로 하여 명령어들의 flow를 나타낸 것으로, 구현에 따라 변하지 않기 때문에 처음에 구상한 asm chart를 프로젝트 완료 시점까지 그대로 참고로 하여 사용하였다. 1 클락당 4개의 서브 사이클로 구성되어있고, 각각의 사이클은 명령어에 따라 다르게 동작한다.
C File은 C Compiler를 통해서 컴파일 되고 그것이 동적 라이브러리 형태로 만들어진다. 그리고 Verilog File은 기존의 Verilog 파일로 컴파일 되고, 위의 만들어진 것들이 Simulator를 통해서 동시에 시뮬레이션이 된다. PIC 시뮬레이션에서 vpi 함수를 불러오기 위해서는 테스트벤치에서 task나 function 형태로 호출하면 된다. 단 task 형태는 return 값을 가질 수 없으므로 주의해야 한다. 또한 vpi <-> pic 간의 프로토콜을 설계하여 서로 주고받는 데이터 방식을 결정해야 하고, calltf, compiletf, callbacktf 등 여러 가지 vpi의 함수 콜 형식이 있으므로 상황에 맞게 설계하는 것이 필요하다.
위는 vpi의 동작방식이고 vpi는 c로 짜서 비쥬얼스튜디오의 컴파일러인cl.exe로 컴파일해야한다.
vpi 코드는 다음과 같다
...중략
PLI_INT32 cosim_pic_init(PLI_BYTE8 *user_data)
{
.......중략..........
fread(&hf, sizeof(BITMAPFILEHEADER), 1, m_ptrInFP);
fread(&hInfo, sizeof(BITMAPINFOHEADER), 1, m_ptrInFP);
fwrite(&hf, sizeof(char), sizeof(BITMAPFILEHEADER), m_ptrOutFP);
fwrite(&hInfo, sizeof(char), sizeof(BITMAPINFOHEADER), m_ptrOutFP);
vpi_printf("BMP header copy complete \n");
for(i=0; i<=441 ; i++)
{
if(count<3) {
fread(&data[i], sizeof(char), 1, m_ptrInFP); //b g r
count++;
}
if(count==3){
gray[count2]=data[i-2]*0.3 + data[i-1]* 0.59+ data[i-1]*0.11;
//픽셀정보 줄 필요 없이 gray scale 만 전송
count=0;
count2++;
}
}
vpi_printf("gray scaling complete \n");
return 0;
}
PLI_INT32 cosim_pic_read(PLI_BYTE8 *user_data)
{
if(readCount==0) { //1바이트씩 세번 보낸다.
.......중략..........
vpi_printf("send[%d] %x \n", grayCount, gray[grayCount]);
}
else if(readCount==1){
. ......중략..........
vpi_put_value(in_port_h, &value, NULL, vpiNoDelay);
vpi_printf("send[%d] %x \n", grayCount, gray[grayCount+1]);
}
else if(readCount==2){
systf_handle = vpi_handle(vpiSysTfCall, NULL);
.......중략..........
vpi_put_value(in_port_h, &value, NULL, vpiNoDelay);
vpi_printf("send[%d] %x ", grayCount, gray[grayCount]);
grayCount++;
}
return 0;
}
PLI_INT32 cosim_pic_write(PLI_BYTE8 *user_data)
{
.......중략..........
fwrite(&data, sizeof(BYTE), 1, m_ptrOutFP);
fwrite(&data, sizeof(BYTE), 1, m_ptrOutFP);
fwrite(&data, sizeof(BYTE), 1, m_ptrOutFP);
vpi_printf("receive[%d] %x \n", grayCount2, data);
grayCount2++;
return 0;
}
위 코드를 cl.exe로 컴파일하고, link.exe로 동적 라이브러리파일(dll) 파일로 만들어서 verilog 시뮬레이션 실행파일인 vsim과 함께 링크해서 사용하면 칩시뮬레이션에 vpi모듈을 이용할 수 있다.
여기까지 완료되면, 마지막 단계로 넘어가자. 실제 이미지 파일을 우리가 만든 cpu에서 입력으로 받아들여서 이미지의 외곽선을 검출하고 흑백이미지로 변환하는 작업까지 수행할 수 있는지를 테스트한다.
연산처리 과정은 다음과 같다.
VPI에서 bmp 파일 헤더를 미리 복사해서(54 bytes) result.bmp 파일에 저장하고 그 후 픽셀 정보를 gray scaling 하여 하나로 합쳐 내부 배열에 저장한다. 그 후 배열을 1byte 씩 총 세 픽셀정보를 PIC 에 전송한다. PIC 에서는 이를 받아 오른쪽과 아래쪽 픽셀과 값을 비교하여, 큰 쪽에서 작은 쪽 값을 빼고 이를 반복하여 저장 후(Robert algorithm 응용) 다시 VPI로 계산 결과를 보낸다. 그리고 VPI 함수에서는 이를 받아 result.bmp에 저장하여 edge detection 된 결과 bmp 파일을 생성하게 된다.
이제 VPI 모듈 구현이 끝났고, 마지막으로 CPU가 연산을 실행하기위해서는 맨 처음에 설명하였듯이 ROM에 미리 저장되어 있는 순서대로 instruction들을 가져와서 실행하는데, ROM에서는 어셈블리어만을 인식한다. 따라서 이번 edge detection을 수행하기 위해서는 operation들을 c code로 먼저 짜고, PIC 칩 제조사에서 제공하는 PICC compiler를 이용하여 c code를 assembly language로 변환하는 작업이 필요하다.
이제 모든 과정이 끝났고 실제로 이미지를 input으로 주고 설계한 cpu가 제대로 edge detection을 수행하는지를 테스트하는 과정만 남았다. 테스트 결과
간단한 그림을 테스트해보았는데, 제대로 동작하는 것을 알 수 있었다. 회로의 전체 schemetic 은 다음 그림과 같다.