OpenSparc Coding Style

OpenSparc을 좀 들여다 보기로 하고선 진도가 잘 안나가네요.

제가 OpenSparc에서 관심 가졌던 부분은 몇가지가 있습니다. 그중 한가지가 코딩 스타일입니다.
참고로, 소스코드를 다운로드하지 않아도 Web으로도 간단히 T1T2의 코드를 직접 확인하실 수 있습니다.

먼저 Naming Convention입니다.
방대한 분량 (T1의 경우 33만6천라인)의 설계를 하기 위해선 분명 적지않은 개발자가 참여했으므로 체계적이고 명확하고 효율적인 Naming Convention이 존재하리라 기대했으나 기대했던 수준은 아닌 것 같습니다.

인상적인 부분 몇가지는 signal name과 module name이 상당히 깁니다. 특히 module name이 긴데 ‘sparc_exu_aluadder64′ 정도의 길이는 보통입니다. 명확한 대신 한눈에 안들어온다는 단점, module name을 입력할 때 수동으로 입력하면 틀리기 십상이라 반드시 자동완성 기능을 사용해야 되겠더군요.

특징적인 부분은 always문이 매우 적은 빈도로 사용되어있습니다.
flip-flop이 필요한 곳은 아래와 같이 D flip-flop을 가져다가 사용하고 있기 때문이긴 하지만 상당히 의외네요. 그렇다면 FSM(Finite State Machin)을 어떻게 구현했나 찾아보니, 일반적인 FSM 형태로 구현된 부분은 Thread State Machine과 Miss Instruction List State Machine 두개 밖에 없군요.

wire [1:0] thrid_m, thrid_g ;
dff #(2)  stgm_thrid (
       .din    (
ifu_tlu_thrid_e[1:0]),
       .q      (thrid_m[1:0]),
       .clk    (
clk),
       .se     (1′b0),       .si (),         
.so ()
       );

dff  #(2) stgg_thrid (
       .din    (
thrid_m[1:0]),
       .q      (thrid_g[1:0]),
       .clk    (
clk),
       .se     (1′b0),       .si (),         
.so ()
       );

dff  stgw_ivld (
       .din    (
flush_w_inst_vld_m),
       .q      (lsu_inst_vld_tmp),
       .clk    (
clk),
       .se     (1′b0),       .si (),         
.so ()
       );

이렇게 버스 단위로 D flip-flop을 instanciation하거나

dff  #(4) ivld_stgw2 (
       .din    ({
ld0_inst_vld_g,ld1_inst_vld_g,ld2_inst_vld_g,ld3_inst_vld_g}),
       .q    ({ld0_inst_vld_w2,ld1_inst_vld_w2,ld2_inst_vld_w2,ld3_inst_vld_w2}),
       .clk  (
clk),
       .se     (1′b0),       .si (),         
.so ()
  );

dff  #(4) th_stgm (
       .din    ({
thread0_e,thread1_e,thread2_e,thread3_e}),
       .q      ({thread0_m,thread1_m,thread2_m,thread3_m}),
       .clk  (
clk),
       .se     (1′b0),       .si (),         
.so ()
  );

dff  #(4) th_stgg (
       .din    ({
thread0_m,thread1_m,thread2_m,thread3_m}),
       .q      ({thread0_g,thread1_g,thread2_g,thread3_g}),
       .clk  (
clk),
       .se     (1′b0),       .si (),         
.so ()
  );

dff  #(4) th_stgw2 (
       .din    ({
thread0_g,thread1_g,thread2_g,thread3_g}),
       .q      ({thread0_w2,thread1_w2,thread2_w2,thread3_w2}),
       .clk  (
clk),
       .se     (1′b0),       .si (),         
.so ()
  );

이렇게 concatenation해서 flip-flop을 연결합니다.

이런 coding style은 datapath의 pipeline register를 기술할때 특히 효과적이겠네요.

OpenSparc T2는 어떤가 하고 조금 살펴보니, T1보다 훨씬 module name이 길고 직접 dff을 가져다 쓰는게 아니라 module을 한겹 덧씌워서 unique하게 만들어 사용하는군요.

module spc_lb_ctlmsff_ctl_macro__width_15Index (
 
din,
 
l1clk,
 
scan_in,
 
siclk,
 
soclk,
 
dout,
 
scan_out);
wire [14:0] fdin;
wire [13:0] so;

  input [14:0] din;
  input l1clk;
  input scan_in;

  input siclk;
  input soclk;

  output [14:0] dout;
  output scan_out;

assign
fdin[14:0] = din[14:0];

dff #(15)  d0_0 (
.l1clk(
l1clk),
.siclk(
siclk),
.soclk(
soclk),
.d(
fdin[14:0]),
.si({
scan_in,so[13:0]}),
.so({
so[13:0],scan_out}),
.q(
dout[14:0])
);

endmodule

또 한가지, T2는 제가 알고 있던 상식을 깨고 있었는데, 위의 macro module을 잘 보면 scan input, scan output까지 연결하고 있습니다.

네 그렇습니다. 이런 식의 register macro들을 모두 연결하여 RTL상에서 scan chain을 구성하고 있었습니다. T1만 하더라도 scan chain은 합성단계에서 auto insertion하고 있었던 것으로 보이는데, 다음 세대인 T2는 RTL에서 scan chain을 모두 stich해두었는데… 이것 참 어떻게 받아들여야할지?(T1, T2가 모두 그렇다면 Sun의 전통(?)이겠거니 할텐데 그것도 아니고 말입니다.)

혹시 수십,수백만 게이트 짜리 디자인의 scan-chain을 manual stitch한다는 얘기 들어보신분?

8 thoughts on “OpenSparc Coding Style

  1. F/F을 저렇게 직접 instanciation하는게 pipeline구현에 더 효율적인가요? 전 쏙 와닿지 않네요. 게다가 scan chain을 manually 연결한다는 발상은 첨 들어보네요. Physical placement정보를 사전에 안다곤 해도 좀 무모해보임.

    • 과연 어떤 생각으로 scan-chain을 manual stitch한 것일까요?Tool이 지원을 안해주는 시절에 만든 것도 아니고 그것 참… ^^; 어쩌면 깊은 뜻이 담겨있을지도 모르죠. 그래도 Sun인데…

      F/F instanciation이 pipeline구현 자체에 효율적인가? 기술방법을 바꾼다고 회로가 바뀌는 것은 아니니 구현상의 효율이 높아지진 않을 것 같고요.

      기술(description)관점에서 볼 때, bus형태의 신호들이 많은 datapath에 pipeline register를 넣을때 효과적(간결함,가독성 측면) 일 수 있다는 얘기였습니다~

  2. 전부는 아니더라도 일부의 경우 manual stitching하는 경우가 있어요. 디버거에서 scan chain을 활용할때 stitching order가 중요해서 그렇지요.
    사실 많은 레지스터에 걸기는 하는데, 전부에 대해서 하는 경우는 거의 없는데 ^^;

    • 아.. 그렇네요.
      순서가 중요할 때가 있었던 거 같은데 했는데 역시 babyworm님이 답을 주시네요.
      In-Circuit Emulator 구현할때 Instruction Register나 Register File을 순서대로 직접 chain을 엮었던 기억이 납니다.

      T2는 모든 레지스터를 다 연결한 것 같은데 아직 그 심오한(?) 의미를 모르겠네요. ^^

  3. 아.. 그리고, 말씀하신 것과 같이 F/F를 직접 instance한다고 좋아지는 건 없고요.. :)
    명확하게 어느 지점에서 FF가 들어갔는지 알려주는 효과는 있겠죠. 가끔 FF를 제외한 combinational logic의 일부를 custom cell로 그리는 경우도 있고 하니까.. 음.. 도움이 될수는 있겠네요..
    always잘 안쓰고 assign 많이 쓰는 건 대부분 좀 오래된 회사의 coding style인거 같아요. ^^;

      • 사실은 저도 assign을 많이 사용하는 스타일이라 :)
        좀 더 솔직히 이야기하면 local wire인 경우에는 그냥

        wire [31:0] aaa = bb ? cc : dd;

        같은 식으로도 많이 쓰죠.. 한번 이상 쓰이는 신호는 이렇게 안쓰지만.. :)

        • 생각해보니 저도 꼭 필요한 부분이 아니면 assign을 쓰자는 주의입니다.

          8~9년전까진 mux도 모두 assign을 쓰자였는데, full-case가 아닌 경우의 optimization을 위해서 case문을 사용하고 있습니다.

          그외엔 state machine(or next state logic), pipeline register, output register에만 always문을 쓰고 counter를 제외한 combinational logic은 모두 assign을 사용하는 것 같습니다. ^^

          말씀하신 local wire의 기술(정의와 함께 assign하는…)은 Verilog-2000 부터 지원되던 것이 같네요. 편해진 기능은 사용해야지요. 그런데 저는 아직도 버릇이 되서 wire선언과 assign을 구분하게 되더군요. ^^;

Leave a Reply to babyworm Cancel reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>