1. handle multiple files:
:bn - jump to next file
:bp - jump to previous file
:bw - close current file
Ctrl ^ - toggle tween current and previous files
:e# - jump to previous file
:b1 - jump to the first file
1. column edit (visual mode):
v - visual mode
Ctrl v - visual-block mode
V - visual-line
4/27/2007
Embed Perl into C/C++
1. Compile the interpreter:
gcc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
`perl -MExtUtils::Embed -e ccopts -e ldopts` is used to set the options fro gcc
interp.c:
#include /* from the Perl distribution */
#include /* from the Perl distribution */
static PerlInterpreter *my_perl; /*** The Perl interpreter ***/
int main(int argc, char **argv, char **env)
{
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct(my_perl);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
perl_parse(my_perl, NULL, argc, argv, (char **)NULL);
perl_run(my_perl);
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
2. Run the interpreter:
$interp
print "Pretty Good Perl \n";
print "10890 - 9801 is ", 10890 - 9801;
Pretty Good Perl
10890 - 9801 is 1089
$
gcc -o interp interp.c `perl -MExtUtils::Embed -e ccopts -e ldopts`
`perl -MExtUtils::Embed -e ccopts -e ldopts` is used to set the options fro gcc
interp.c:
#include
#include
static PerlInterpreter *my_perl; /*** The Perl interpreter ***/
int main(int argc, char **argv, char **env)
{
PERL_SYS_INIT3(&argc,&argv,&env);
my_perl = perl_alloc();
perl_construct(my_perl);
PL_exit_flags |= PERL_EXIT_DESTRUCT_END;
perl_parse(my_perl, NULL, argc, argv, (char **)NULL);
perl_run(my_perl);
perl_destruct(my_perl);
perl_free(my_perl);
PERL_SYS_TERM();
}
2. Run the interpreter:
$interp
print "Pretty Good Perl \n";
print "10890 - 9801 is ", 10890 - 9801;
Pretty Good Perl
10890 - 9801 is 1089
$
4/21/2007
Logic Calucation in Veirlog
~z = x;
~X = X;
0&Z = 0&X = 0;
1&Z = 1&X = X;
Z&1 = Z&Z = Z&X = X;
X&1 = X&Z = X&X = X;
Z|0 = Z|Z = Z|X = X;
X|0 = X|Z = X|X = X;
Z^* = X^* = X;
Z~^* = X~^* = X;
~X = X;
0&Z = 0&X = 0;
1&Z = 1&X = X;
Z&1 = Z&Z = Z&X = X;
X&1 = X&Z = X&X = X;
Z|0 = Z|Z = Z|X = X;
X|0 = X|Z = X|X = X;
Z^* = X^* = X;
Z~^* = X~^* = X;
4/09/2007
Ways to assign signals in Verilog
1. Continuous assignment (assign)
wire a;
assign a = 1'b1; //behaves like a wire
2. Procedural assignemnt (always)
a). blocking assignment
reg a, b;
a = 1'b1; //will block next assignment only in sequential blocks
b = 1'b0;
b). non-blocking assignment
reg a, b;
a <= 1'b1; //all non-blocking assignemnts will be executed at b <= 1'b0; //the END of this update event.
Because non-blocking assignments are executed at the end of update event, if they are mixed with blocking assigments, non-blocking assignments will be executed after all blocking assignments.
If multiple non-blocking assignments are used to assign the same variable in a block, the order of assignments will be hornored.
reg a;
a <= 1'b1;
a <= 1'b0; //a is 0 finally If multiple non-blocking/blocking assignments are used to assign the same variable in different blocks, and the order of their executions can not be decided, then the final value of this variable can not be determined.
reg a;
initial a <= 1'b1;
initial a <= 1'b0; //a is uncertain
However, if the order of their executions can be determined, the final value of the variable is determined.
reg a;
initial #8 a <= #8 1'b1; //@16, a is set to 1, scheduled @8
initial #10 a <= #6 1'b0; //@16, a is set to 0, scheduled @10
a would be 0 finally because the second assignemnt is scheduled later.
3. Procedural continuous assignment (assign/deassign, force/release)
assign procedural continuous assignment will override all previous procedural assignments to the same variable. deassign will cancel this assignment.
force/release have the similar function with the exception that they can be used on wires as well.
module dff (q,d,clear, preset, clock);
input d, clear, preset, clock;
output q;
reg q;
always @(clear or preset)
if(!clear) assign q = 0;
else if (!preset) assign q = 1;
else deassign q;
always @(posedge clock)
q = d;
endmodule
wire a;
assign a = 1'b1; //behaves like a wire
2. Procedural assignemnt (always)
a). blocking assignment
reg a, b;
a = 1'b1; //will block next assignment only in sequential blocks
b = 1'b0;
b). non-blocking assignment
reg a, b;
a <= 1'b1; //all non-blocking assignemnts will be executed at b <= 1'b0; //the END of this update event.
Because non-blocking assignments are executed at the end of update event, if they are mixed with blocking assigments, non-blocking assignments will be executed after all blocking assignments.
If multiple non-blocking assignments are used to assign the same variable in a block, the order of assignments will be hornored.
reg a;
a <= 1'b1;
a <= 1'b0; //a is 0 finally If multiple non-blocking/blocking assignments are used to assign the same variable in different blocks, and the order of their executions can not be decided, then the final value of this variable can not be determined.
reg a;
initial a <= 1'b1;
initial a <= 1'b0; //a is uncertain
However, if the order of their executions can be determined, the final value of the variable is determined.
reg a;
initial #8 a <= #8 1'b1; //@16, a is set to 1, scheduled @8
initial #10 a <= #6 1'b0; //@16, a is set to 0, scheduled @10
a would be 0 finally because the second assignemnt is scheduled later.
3. Procedural continuous assignment (assign/deassign, force/release)
assign procedural continuous assignment will override all previous procedural assignments to the same variable. deassign will cancel this assignment.
force/release have the similar function with the exception that they can be used on wires as well.
module dff (q,d,clear, preset, clock);
input d, clear, preset, clock;
output q;
reg q;
always @(clear or preset)
if(!clear) assign q = 0;
else if (!preset) assign q = 1;
else deassign q;
always @(posedge clock)
q = d;
endmodule
3/27/2007
Most useful VPI code blocks
1. Register a system call
void hello_register()
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$hello";
tf_data.calltf = hello;
tf_data.compiletf = NULL;
tf_data.sizetf = NULL;
vpi_register_systf(&tf_data);
}
2. Obtain system task arguments
vpiHandle systf_handle, arg_iterator, arg_handle, net_handle;
s_vpi_value current_value;
/* obtain a handle to the system task instance */
systf_handle = vpi_handle(vpiSysTfCall, NULL);
/* obtain handle to system task argument */
arg_iterator = vpi_iterate(vpiArgument, systf_handle);
net_handle = vpi_scan(arg_iterator);
vpi_free_object(arg_iterator); /* free iterator memory */
void hello_register()
{
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$hello";
tf_data.calltf = hello;
tf_data.compiletf = NULL;
tf_data.sizetf = NULL;
vpi_register_systf(&tf_data);
}
2. Obtain system task arguments
vpiHandle systf_handle, arg_iterator, arg_handle, net_handle;
s_vpi_value current_value;
/* obtain a handle to the system task instance */
systf_handle = vpi_handle(vpiSysTfCall, NULL);
/* obtain handle to system task argument */
arg_iterator = vpi_iterate(vpiArgument, systf_handle);
net_handle = vpi_scan(arg_iterator);
vpi_free_object(arg_iterator); /* free iterator memory */
3/23/2007
Reset CF GPS receiver
1) Download WinFast Navigator at . Copy and paste this URL onto your browser. ftp://ftp.winfast.com.tw/gps/Tools/WinFast GPS-Install.zip
2) Then, unzip the file but you only need to move/copy "Navigator.ARM.CAB" to your PDA. This file is located in "WinFast Navigator CE" folder.
3) Once you have copied this cab file to your PDA, just use File Explorer to locate it and tap once on it to launch the installtion program.
4) To launch the program, click on Start | Navigator.
5) Insert your CF Card to the CF slot now and observe the Device plug-in! pop up windows and note down the COM number. Click OK to close out of the pop up window.
6) Click on Tools | Port Setting… | tap on OK 3 times to get safely get rid of the error message.
a. GPS Portocol=NMEA,
b. Port=Modem COM X , X is the number you observed on step 5
c. Baud Rate=4800.
7) Click OK
8) Click on Tools | Make sure Factory radio button is selected under Reset Mode, leave everything else as default and click OK.
9) Reset to default completes. Click on View | Signal Level to observe the GPS lock status.
2) Then, unzip the file but you only need to move/copy "Navigator.ARM.CAB" to your PDA. This file is located in "WinFast Navigator CE" folder.
3) Once you have copied this cab file to your PDA, just use File Explorer to locate it and tap once on it to launch the installtion program.
4) To launch the program, click on Start | Navigator.
5) Insert your CF Card to the CF slot now and observe the Device plug-in! pop up windows and note down the COM number. Click OK to close out of the pop up window.
6) Click on Tools | Port Setting… | tap on OK 3 times to get safely get rid of the error message.
a. GPS Portocol=NMEA,
b. Port=Modem COM X , X is the number you observed on step 5
c. Baud Rate=4800.
7) Click OK
8) Click on Tools | Make sure Factory radio button is selected under Reset Mode, leave everything else as default and click OK.
9) Reset to default completes. Click on View | Signal Level to observe the GPS lock status.
3/17/2007
FSM Coding Convention
Two-always blocks with continuous assignment outputs:
module fsm1a (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
parameter [1:0] IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 2'bx;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: next = DLY;
DLY: if (ws) next = READ;
else next = DONE;
DONE: next = IDLE;
endcase
end
assign rd = (state==READ state==DLY);
assign ds = (state==DONE);
endmodule
Two-always blocks with combined output assignments:
module fsm1 (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
reg ds, rd;
parameter [1:0] IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 2'bx;
ds = 1'b0;
rd = 1'b0;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: begin rd = 1'b1;
next = DLY;
end
DLY: begin rd = 1'b1;
if (ws) next = READ;
else next = DONE;
end
DONE: begin ds = 1'b1;
next = IDLE;
end
endcase
end
endmodule
Three-always block coding style
module fsm1b (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
reg ds, rd;
parameter [1:0] IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 2'bx;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: next = DLY;
DLY: if (ws) next = READ;
else next = DONE;
DONE: next = IDLE;
endcase
end
always @(posedge clk or negedge rst_n)
if (!rst_n) begin
ds <= 1'b0;
rd <= 1'b0;
end
else begin
ds <= 1'b0;
rd <= 1'b0;
case (state)
IDLE: if (go) rd <= 1'b1;
READ: rd <= 1'b1;
DLY: if (ws) rd <= 1'b1;
else ds <= 1'b1;
endcase
end
endmodule
module fsm1a (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
parameter [1:0] IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 2'bx;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: next = DLY;
DLY: if (ws) next = READ;
else next = DONE;
DONE: next = IDLE;
endcase
end
assign rd = (state==READ state==DLY);
assign ds = (state==DONE);
endmodule
Two-always blocks with combined output assignments:
module fsm1 (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
reg ds, rd;
parameter [1:0] IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 2'bx;
ds = 1'b0;
rd = 1'b0;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: begin rd = 1'b1;
next = DLY;
end
DLY: begin rd = 1'b1;
if (ws) next = READ;
else next = DONE;
end
DONE: begin ds = 1'b1;
next = IDLE;
end
endcase
end
endmodule
Three-always block coding style
module fsm1b (ds, rd, go, ws, clk, rst_n);
output ds, rd;
input go, ws;
input clk, rst_n;
reg ds, rd;
parameter [1:0] IDLE = 2'b00,
READ = 2'b01,
DLY = 2'b10,
DONE = 2'b11;
reg [1:0] state, next;
always @(posedge clk or negedge rst_n)
if (!rst_n) state <= IDLE;
else state <= next;
always @(state or go or ws) begin
next = 2'bx;
case (state)
IDLE: if (go) next = READ;
else next = IDLE;
READ: next = DLY;
DLY: if (ws) next = READ;
else next = DONE;
DONE: next = IDLE;
endcase
end
always @(posedge clk or negedge rst_n)
if (!rst_n) begin
ds <= 1'b0;
rd <= 1'b0;
end
else begin
ds <= 1'b0;
rd <= 1'b0;
case (state)
IDLE: if (go) rd <= 1'b1;
READ: rd <= 1'b1;
DLY: if (ws) rd <= 1'b1;
else ds <= 1'b1;
endcase
end
endmodule
Encoding Styles for FSM
- Binary encoding is good for creating small (less than 16 states) FSMs in FPGAs. Larger FSMs require too much Next state logic and perform slower when binary encoded.
- One-Hot encoding is good for building larger FSMs since it requires very little Next state logic. OHE is more reliable than Binary encoding, because fewer bits change at once making the circuit less likely to glitch. OHE leaves a lot of unused states in the FSM and also uses a lot of registers (but there are a lot of registers in FPGAs).
- Gray encoding of your state machine uses less resources than OHE and also gets good speed. Gray encoding is also the most reliable encoding technique, because only one bit changes per transition. However, Gray encoding requires the designer to be very aware of the coding technique chosen when simulating the FSM, and this is often a pain.
2/20/2007
Function pointer and function returning a pointer
C/C++ Function Pointers
1. Define:
int (*pFunction)(int, float, char) = NULL;
2. Assign:
int func1(int a, float b, char c) {
...
}
pFunction = &func1;
3. Use:
(*pFunction)(1, 2.5, 'c');
Function that returns a pointer
1. Define:
int* func1(int a, float b, char c) {
return &a;
}
2. Use:
int *pA;
pA = func1(1, 2.5, 'c');
1. Define:
int (*pFunction)(int, float, char) = NULL;
2. Assign:
int func1(int a, float b, char c) {
...
}
pFunction = &func1;
3. Use:
(*pFunction)(1, 2.5, 'c');
Function that returns a pointer
1. Define:
int* func1(int a, float b, char c) {
return &a;
}
2. Use:
int *pA;
pA = func1(1, 2.5, 'c');
加sdf的postsimulation
Add the following code in the testbench of your design:
initial
begin
$sdf_annotate("sdf_file.sdf", uut);
end
initial
begin
$sdf_annotate("sdf_file.sdf", uut);
end
Mentor Calibre安装说明
1. 把档案放到linux主机里面,然后执行
‘chmod 755 MENTOR.CALIBRE.2005.3.6.10.LINUX.X86.X86.64.exe’
2. 然后执行./MENTOR.CALIBRE.2005.3.6.10.LINUX.X86.X86.64.exe,开始安装。
(安装Calibre需要x-window)
.License产生
1. http://www.hao007.net/cgi-bin/topic.cgi?forum=2&topic=25057&show=0
,取得keygen
2. 假设档案安装在/calibre之下,在/calibre/bin这目录中,
执行‘./lmhostid’,将得到的hostid复制起来。
3. 执行keygen,将hostid贴上,然后点选产生,会得到一个license.dat的档案。
4. 将产生的lincense.dat丢到Linux主机上,给他选个喜欢的路径放进去,例如/calibre/license/
.环境设定
setenv LM_LICENSE_FILE /calibre/license/license.dat
setenv MGC_HOME /calibre
set CalibrePath = ( $MGC_HOME/bin )
将上面三行存到.cshrc,这样在登入以后就可以自动载入(搭配csh or tcsh shell使用)
.测试执行
setenv跟path设定都完成后,直接执行‘calibre -gui -drc&’或是‘calibre -gui -lvs&’,
就可以看到程式顺利执行,而且不会跳错误讯息说license取得失败。
‘chmod 755 MENTOR.CALIBRE.2005.3.6.10.LINUX.X86.X86.64.exe’
2. 然后执行./MENTOR.CALIBRE.2005.3.6.10.LINUX.X86.X86.64.exe,开始安装。
(安装Calibre需要x-window)
.License产生
1. http://www.hao007.net/cgi-bin/topic.cgi?forum=2&topic=25057&show=0
,取得keygen
2. 假设档案安装在/calibre之下,在/calibre/bin这目录中,
执行‘./lmhostid’,将得到的hostid复制起来。
3. 执行keygen,将hostid贴上,然后点选产生,会得到一个license.dat的档案。
4. 将产生的lincense.dat丢到Linux主机上,给他选个喜欢的路径放进去,例如/calibre/license/
.环境设定
setenv LM_LICENSE_FILE /calibre/license/license.dat
setenv MGC_HOME /calibre
set CalibrePath = ( $MGC_HOME/bin )
将上面三行存到.cshrc,这样在登入以后就可以自动载入(搭配csh or tcsh shell使用)
.测试执行
setenv跟path设定都完成后,直接执行‘calibre -gui -drc&’或是‘calibre -gui -lvs&’,
就可以看到程式顺利执行,而且不会跳错误讯息说license取得失败。
Why virtual clocks?
First the funda - virtual clocks are used to set timing-exceptions between two clocks on a particular timing path. There, that's all there is to it.
Consider, two flops arriving at a output port (say out1), through some combo logic. Therefore, there exists, two timing paths terminating at the same output port. Assume that both flops are clocked by two different clocks (C1 and C3). Therefore, it is mandatory that the designer set two output-delays at the output port - one each w.r.t each clock.
Now, while the tool is doing STA, it is likely that it would consider the flop (of clock C1), going through the combo-logic and terminating at out1. However, it will consider two scenarios - timing w.r.t the output-delay set with C1 and w.r.t the output-delay set with C2. Nothing wrong with this scenario, except that it assumes that a signal originating at C1 is going to go through out1 and end up, on the other side, on a flop clocked by C2.
Suppose, this does not happen - as is often the case. So how do we handle this? A simple answer would be to use false-paths. So we put a false-path from C1 -> C2 and from C2 -> C1. Cool. Except, what if somewhere else in the design , C1 and C2 were actually communicating with each other. This false-path would cause those other timing-paths to be ignored by the tool.
Not good.
Enter virtual clocks.
Let us create two clocks C3 and C4. C3 has the timing characteristics of C1 and C4 has the timing-characteristics of C2. Now, on port out1, instead of setting output-delays w.r.t C1 and C2, set them with C3 and C4. There would be no difference in the STA. However, this time, when we set the false-paths, do them with C3 and C4. This would cause the exact same thing as we wanted above, except that we cheated... and we did not cause other places where C1 and C2 are communicating to be treated as false-paths also.
Considering most designs these days are pad-limited (i.e. where chip area cannot be reduced because of the area taken up by pads), efforts are made by designers to reduce umbers of ports. This causes a junction of sorts, where different clock timing-paths intersect. This is a very good place to try out virtual-clocks.
Consider, two flops arriving at a output port (say out1), through some combo logic. Therefore, there exists, two timing paths terminating at the same output port. Assume that both flops are clocked by two different clocks (C1 and C3). Therefore, it is mandatory that the designer set two output-delays at the output port - one each w.r.t each clock.
Now, while the tool is doing STA, it is likely that it would consider the flop (of clock C1), going through the combo-logic and terminating at out1. However, it will consider two scenarios - timing w.r.t the output-delay set with C1 and w.r.t the output-delay set with C2. Nothing wrong with this scenario, except that it assumes that a signal originating at C1 is going to go through out1 and end up, on the other side, on a flop clocked by C2.
Suppose, this does not happen - as is often the case. So how do we handle this? A simple answer would be to use false-paths. So we put a false-path from C1 -> C2 and from C2 -> C1. Cool. Except, what if somewhere else in the design , C1 and C2 were actually communicating with each other. This false-path would cause those other timing-paths to be ignored by the tool.
Not good.
Enter virtual clocks.
Let us create two clocks C3 and C4. C3 has the timing characteristics of C1 and C4 has the timing-characteristics of C2. Now, on port out1, instead of setting output-delays w.r.t C1 and C2, set them with C3 and C4. There would be no difference in the STA. However, this time, when we set the false-paths, do them with C3 and C4. This would cause the exact same thing as we wanted above, except that we cheated... and we did not cause other places where C1 and C2 are communicating to be treated as false-paths also.
Considering most designs these days are pad-limited (i.e. where chip area cannot be reduced because of the area taken up by pads), efforts are made by designers to reduce umbers of ports. This causes a junction of sorts, where different clock timing-paths intersect. This is a very good place to try out virtual-clocks.
Astro Design Tips
1. In Apollo, timing constraints are stored in the cell database in a TDF table.
Astro will only read SDC syntax for timing constraints.
2. For Clock Tree Synthesis, TLU/TLU+ models are needed for
timing. Data Prep/Tech File/Create Capacitance Model... can
generate TLU model based on technology file.
Astro will only read SDC syntax for timing constraints.
2. For Clock Tree Synthesis, TLU/TLU+ models are needed for
timing. Data Prep/Tech File/Create Capacitance Model... can
generate TLU model based on technology file.
Samba快速配置
更改/etc/smb.conf,主要要确定下面的内容:
workgroup=MYWKGP
smb passwd file=/etc/smbpasswd
security=user (使用unix用户安全)
encrypt passwords= yes
然后就可以添加目录了,首先添加宿主目录:
(其实就是把对应的行的注释去掉)
[homes]
comment = home directory
browseable = no
writeable= yes
还可以再加一个共享目录:
[public]
comment = public directory
path=/public
public=yes
writeable=no
read only=yes
printable=no
最后当然是加入用户,比如想把fake用户当成windows 系统中的winfake,执行
smbadduser fake:winfake
输入访问口令,以后在windows中只要提供winfake和对应口令
workgroup=MYWKGP
smb passwd file=/etc/smbpasswd
security=user (使用unix用户安全)
encrypt passwords= yes
然后就可以添加目录了,首先添加宿主目录:
(其实就是把对应的行的注释去掉)
[homes]
comment = home directory
browseable = no
writeable= yes
还可以再加一个共享目录:
[public]
comment = public directory
path=/public
public=yes
writeable=no
read only=yes
printable=no
最后当然是加入用户,比如想把fake用户当成windows 系统中的winfake,执行
smbadduser fake:winfake
输入访问口令,以后在windows中只要提供winfake和对应口令
TCL/TK 与 C 程序的集成
一、 简介
比较TCL/TK 提供的快速而又容易的开发图形拥护界面,X 程序显得很烦琐。Tcl/tk 是一种脚本语言,就象其它的一些脚本语言一样,也有很多事情不能够做或很难做。解决途径是联合 C 与 tcl/tk 一起来开发. tcl/tk 系统提供C 程序调用TCL/TK 的解释器来运行TCL/TK脚本。提供的库包括初始化变量的方法,调用不同的脚本和访问变量。利用这些混合变量对它们访问X固有的特性也提供了好处。简单的回调和时间函数允许程序员制定事件,注册一个C函数为TCL/TK的过程的能力成为一个强大的工具。这篇文档覆盖了TCL/TK脚本与C 集成的一些基础知识。 编译选项部分描述了变量库并包含了建立程序的必要文件。 初始化与注册名令部分解释了怎样开始,怎样从TCL/TK脚本中调用C函数,最后一部分访问变量阐述了怎样来从C函数里来读与写TCL/TK变量。
二、编译选项
为了能访问TCL/TK 库,必须在你的源代码中要设置一些常规的例程做并编译它。有两个调用库的头文件被声明。
#include
#include
编译混合应用程序需要指出正确的编译目录,正确的库,并设置正确的连接标志。在TCL/TK顶部的设置也是必须要包含的文件。而下面的设置是在使用 g++ 时要设置的。你的系统依赖于编译器和文件的定位可能有不同的变化。
-I/software/tcl-7.4/include
-I/software/tk-4.0/include
-I/software/x11r5_dev/Include
-L/software/tcl-7.4/lib
-L/software/tk-4.0/lib
-L/software/x11r5_dev/lib
-ltk
-ltcl
-lX11
三、初始化与注册命令
建立混合 tcl/tk & C 应用程序的中心要围绕几条选择命令。
首先就是"Tk_Main" 函数, 它用来控制整个 tcl/tk 解释器程序。这条命令没有返回值,因此,它需在你的"main" 函数中加下划线,你所有程序的一旦初始化,"Tk_Main" 函数带来三个变量。第二个变量是一个字符串型数组,每个字符串都有一个特殊的含义。第一个变量表示在这个数组的元素个数。第三个变量是指向初始化函数的指针。此初始化函数在许多地方都要被执行。字符串数组通过"Tk_Main"来通知tcl/tk解释器应用程序的名称和tcl/tk 命令在脚本中的位置。这个数组实际上是传给解释器的命令行参数。数组的第一项给出应用程序名称,第二项给出了运行的脚本位置。如果脚本没有在相同的执行目录下,则需要完整路径。由于继承原因,tcl/tk 需要字符串在许多函数里可以修改,它也有函数作用范围的问题,避免这些问题最早的办法是传递时动态分配字符串下面的代码碎片显示了调用 利用"Hello World" 应用程序和脚本"hello.tcl"来调用 "Tk_Main"。
// prototype for the initialization function
int InitProc( Tcl_Interp *interp );
// declare an array for two strings
char ppszArg[2];
// allocate strings and set their contents
ppszArg[0] = (char )malloc( sizeof( char ) 12 );
ppszArg[1] = (char )malloc( sizeof( char ) 12 );
strcpy( ppszArg[0], "Hello World" );
strcpy( ppszArg[1], "./hello.tcl" );
// the following call does not return
Tk_Main( 2, ppszArg, InitProc );
初始化函数
"Tk_Main" 的调用控制了你的程序在tcl/tk中的整个调用,但是在底部初始化之后和tcl/tk 脚本运行之前,能够执行用户自定义的函数。上面的例子中展示了这个类型的函数: "InitProc". 用户定义的初始化函数必须要返回一个整数类型并产生一个指向解释器的参数Tcl_Interp 。在初始化函数里面建立实际解释器调用"Tk_Init"。"Tk_Init"函数设置一个指向解释器的参数,这正是传递到初始化函数的指针。下面的代码仅只是初始化函数,更多的则是在后面列出。
int InitProc( Tcl_Interp *interp )
{
int iRet;
// Initialize tk first
iRet = Tk_Init( interp );
if( iRet != TCL_OK)
{
fprintf( stderr, "Unable to Initialize TK!\n" );
return( iRet );
} // end if
return( TCL_OK );
} // end InitProc
C函数作为 tcl/tk 过程
现在你要熟悉在tcl/tk 脚本中的过程调用。当设计混合应用程序中有tcl/tk的过程调用C函数是可能的。完成它需要调用"Tcl_CreateCommand" 函数。这是在初始化函数里的常用做法。在tcl/tk 过程中调用函数就象调用其它的过程一样。在tcl/tk 脚本中存在就不必声明这个过程。函数注册有一个特定原型的过程。它们必须要返回一个整数类型,并设置4个变量,第一个是tcl/tk库文件类型"ClientData"。第二个变量是指向解释器的指针。最后的两个变量类似于在C "main"函数中的 "argc" 和 "argv" 这两个变量被用于传递参数给tcl/tk 过程。参数"argc" 包含了传递给tcl/tk过程的参数个数"argv" 是字符串数组,每个字符串包含了一个参数。
int Myfunc( ClientData Data, Tcl_Interp *pInterp, int argc, char argv[] );
当一个函数被注册作为tcl/tk 过程使用时需一个指针与之联系,指针通过"ClientData"来传递进来。"ClientData"的概念允许程序员联系数据结构和对象,调用能引用这个对象的过程。这个结构不经常需要。象早先提到的注册过程需要调用"Tcl_CreateCommand" 函数。这个函数有5个参数。第一个参数是指向解释器的指针,第二个参数是在tcl/tk 中的过程名,第三个参数是一个指向函数的指针,它在当tcl/tk过程被执行时调用。最后两个参数是 "ClientData" 项, 一个指针删除例程。它允许C函数在程序退出为了清空联系对象的结构时被调用。象指向删除函数的指针"ClientData"不经常调用。下面是tcl/tk 过程调用"hey_there" 来调用上面声明的"Myfunc"进行注册的例子。
Tcl_CreateCommand( interp, "hey_there", Myfunc, (ClientData)NULL,
(Tcl_CmdDeleteProc )NULL );
变量访问
在执行tcl/tk过程时能调用C函数并允许你从C中获得tcl/tk的帮助,为了从tcl/tk 中获得C的帮助,这有一系列函数,其中包含了从tcl/tk变量中处理获得的信息和设置的信息。
Tcl_GetVar
"Tcl_GetVar" 函数返回一个指向tcl/tk变量的字符串指针。这个函数有三个参数:指向解释器的指针,tcl/tk 变量的名称,一个标志flag。这个变量在执行脚本联系到解释器的当前范围被访问。如果在当前范没有局部变量则访问全局变量。如没有匹配的全局变量存在则返回一个错误。 Flags参数允许你指定TCL_GLOBAL_ONLY, 为了使这个函数仅仅访问此变量名的全局变量,下面是tcl/tk 脚本中被访问的一部分代码。
set say_hello_to "World"
下面的代码是在C里访问tcl/tk变量"say_hello_to".
char sHelloTo[30];
// after this call sHelloTo should contain "World"
strncpy( sHelloTo, Tcl_GetVar( pInterp, "say_hello_to", 0 ), 29 );
Tcl_SetVar
"Tcl_SetVar"函数允许程序员修改tcl/tk变量的值。此函数有四个参数:第一个是解释器指针,第二个是要修改值的tcl/tk变量名称,第三个是要修改的新值,最后一个是tcl/tk标志flags。"Tcl_SetVar" 的标志flags跟"Tcl_GetVar"的相同。当设置期间遇到出错时"Tcl_SetVar"函数返回NULL值。如果变量不存在,则此函数将在解释器指针引用的脚本内建立一个新的变量。下面的代码将设置tcl/tk变量"say_hello_to"的值为"World"。
Tcl_SetVar( pInterp, "say_hello_to", "World", 0 );
集成C & tcl/tk 应用程序的例子
这个应用程序展示了集成C和TCL/TK所需要的基础。此应用程序展示了一系列的登录框和按钮。当信息从登录框输入和按钮被按下时,其他的空域也被相应的更新。这有许多分享内存设备的接口,是调用大型应用程序的方法。这个接口需要头文件在下面没有包含进来,因此不修改而编译此应用程序是不可能的。但就阅读来说这并不是一个坏的示例。
The Makefile
The script file: pr1
The C file: proof.c
#!/.software/local/.admin/bins/bin/wish -f
#============================================================
# xmail
# by Christopher Trudeau, Copyright 1997
#
# This tcl/tk script displays a desktop clock which goes inverse video when
# new mail arrives. A pull down menu allows the user to launch remote login
# sessions on servers specified in the "hosts" variable. The sessions have
# the appropriate "xhost" and "DISPLAY" values.
#
# Comments and criticism on this program are greatly appreciated. Feel free to
# send me a note at ctrudeau@etude.uwaterloo.ca. This material is copyright
# but non-commercial institutes have permission to reproduce the program in
# its entirety, all other uses require explicit written permission of the
# author.
#============================================================
#------------------------------------------------------------
# Global Settings
#-----------------------------------------------------------
# fill in the following list for hosts that you wish to access, spaces or tabs
# separating the names of the hosts; eg:
#
# set hosts "ampere etude watt.uwaterloo.ca"
set hosts "ampere watt ohm morse novice"
#------------------------------------------------------------
# Procedures
#-------------------------------------------------------------
# proc prRefreshDisplay - called periodically to refresh the displayed time and
# status of the mail box
proc prRefreshDisplay {} {
global last
# get the time
set i [exec date]
set i [ string range $i 11 15 ]
# get the mailbox status
catch {[exec frm -q -s new]} mail
if { [string first "no" $mail] == -1 } {
# "You have new mail." results in white on black
.lTime configure -fg white -bg black -text $i
# if first time set, do the double beep thing
if { $last == 0 } {
bell
after 120
bell
}
set last 1
} else {
# "You have no new mail." results in black on white
.lTime configure -fg black -bg white -text $i
set last 0
}
after 15000 prRefreshDisplay
}
#------------------------------------------------------------
# Main Code
#------------------------------------------------------------
# create the main window and place it if specified
wm title . "xmail"
set args [lindex $argv 0]
string trim $args -if
{ $args == "geometry" } {
wm geometry . [lindex $argv 1]
}
# figure out what terminal name we are at
set userName [exec whoami]
set termName [exec who ]
set temp [string first $userName $termName]
set termName [string range $termName $temp end]
set temp [string first ( $termName]
set temp2 [string first ) $termName]
set termName [string range $termName $temp $temp2]
set termName [string trim $termName "()"]
# initialize variables and widgets
set last 0
set font "-*--medium-r-normal--*-120-*--*--*-"
set font2 "-*--medium-r-normal--*-100-*--*--*-"
label .lTime -font $font
# create the menu button
menubutton .mMenu -relief raised -font $font2 -text ">" -menu .mMenu.m
menu .mMenu.m -tearoff 0
.mMenu.m add cascade -label "xterms" -menu .mMenu.m.xterms
#create the sub menu "xterms"
menu .mMenu.m.xterms -tearoff 0
.mMenu.m.xterms add command -label "local" -command {exec xterm -title local &}
set count 0
set hostN [lindex $hosts $count]
while { $hostN != "" } {
catch { exec xhost $hostN }
set cmd "exec rsh $hostN xterm -display $termName:0 -title $hostN &"
.mMenu.m.xterms add command -label $hostN -command $cmd
incr count 1
set hostN [lindex $hosts $count]
}
.mMenu.m add separator
.mMenu.m add command -label "Exit" -command exit
pack .lTime .mMenu -side left
prRefreshDisplay
#-----------------------------------------------------------
CC = gcc
DEPEND = makedepend
TCL_DIR = /software/tcl-7.4
TK_DIR = /software/tk-4.0
INCS = -I$(TCL_DIR)/include -I$(TK_DIR)/include -I/software/x11r5_dev/Include
LIBS = -L/software/x11r5_dev/lib -L$(TCL_DIR)/lib -L$(TK_DIR)/lib
CCFLAGS= $(INCS) $(LIBS) -g -Wall
LFLAGS = -ltk -ltcl -lX11 -lsocket -lm
ALLDEFINES = -DDEBUG
.SUFFIXES: .c .o .cpp
.c.o:
$(CC) $(CCFLAGS) $(ALLDEFINES) -c $< .cpp.o: g++ -g -Wall $(ALLDEFINES) -c $*.cpp PROOF_C = proof.c PROOF_O = proof.o all: proof proof: $(PROOF_O) $(CC) $(CCFLAGS) $(ALLDEFINES) -o $@ $(PROOF_O) $(LFLAGS) clean: rm -f *.o proof core depend:: $(DEPEND) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(PROOF_C) # DO NOT DELETE THIS LINE proof.o: /usr/include/stdio.h /usr/include/sys/feature_tests.h proof.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/tcl.h proof.o: /usr/include/tk.h /usr/include/stddef.h /usr/include/sys/types.h proof.o: /usr/include/sys/isa_defs.h /usr/include/sys/machtypes.h proof.o: /usr/include/unistd.h /usr/include/sys/unistd.h ../pbx2.h proof.o: /usr/include/sys/ipc.h /usr/include/sys/msg.h /usr/include/sys/shm.h proof.o: /usr/include/sys/time.h /usr/include/errno.h proof.o: /usr/include/sys/errno.h /usr/include/signal.h proof.o: /usr/include/sys/signal.h ../he2.h #!/.software/local/.admin/bins/bin/wish -f #============================================================ # pr1 # by Christopher Trudeau, Copyright 1997 # # This tcl/tk script is used in conjunction with proof.c to test the hardware # emulator for the SX4 project. # # Comments and criticism on this program are greatly appreciated. Feel free to # send me a note at ctrudeau@etude.uwaterloo.ca. This material is copyright # but non-commercial institutes have permission to reproduce the program in # its entirety, all other uses require explicit written permission of the # author. #============================================================ wm title . "Proof" #============================================================ # main window declarations #============================================================ # create the frames for each row of entry fields for {set i 0} {$i <>
#include
#include
#include
#include
#include
#include
#include "../pbx2.h"
#include "../he2.h"
//----------------------------------------------------------
// Global Variables
struct ifmem_t ifmem_p; // pointer to shared hardware memory
//-----------------------------------------------------------
// Function Prototypes
int InitProc(Tcl_Interp interp);
int cmdDoIt( ClientData clientData, Tcl_Interp *pInterp, int argc,
char *argv[] );
//-----------------------------------------------------------
int main()
{
char ppszArg[2];
int iMemId;
printf( "Starting proof...\n" );
// get pointer to shared interface memory
iMemId = shmget( IFMEM_KEY, sizeof( struct ifmem_t ), 0644);
ifmem_p = (struct ifmem_t )shmat( iMemId, 0, 0);
if( (int)ifmem_p == -1 )
{
printf( "Error: unable to access shared interface memory\n" );
exit( 0 );
} // end if -- failed to get interface memory
// initialize arguments for Tk_Main
ppszArg[0] = (char )malloc( sizeof( char ) 8 );
ppszArg[1] = (char )malloc( sizeof( char ) 65 );
strcpy( ppszArg[0], "proof" );
strcpy( ppszArg[1], "/home3/ctrudeau/s/tcl/proof/pr1" );
printf( "Executing tcl/tk script\n" );
Tk_Main( 2, ppszArg, InitProc );
return( 0 );
} // end main
//-----------------------------------------------------------
int InitProc( Tcl_Interp interp )
{
int iRet;
// Initialize tk first
iRet = Tk_Init( interp );
if( iRet != TCL_OK)
{
printf( "Unable to Initialize TK!\n" );
return( iRet );
} // end if
// register any new tcl/tk commands
Tcl_CreateCommand( interp, "cmdDoIt", cmdDoIt, (ClientData)NULL,
(Tcl_CmdDeleteProc )NULL );
return( TCL_OK );
} // end InitProc
//-----------------------------------------------------------
// cmdDoIt
//
// This function is called as a command from tcl/tk. It is issued when the
// user pushes the "Do it" button. Each of the entry fields is checked
// for their contents and the interface memory is updated accordingly.
// The update to i/f mem is used to make connections between various cards
// and to put values into those cards (digits, loop back bits, etc)
//
int cmdDoIt( ClientData clientData, Tcl_Interp *pInterp, int argc,
char *argv[] )
{
int iSlot, iValue, iTrunk, iChan;
char sText[64];
fprintf( stderr, "****** Doing it\n" );
for( iTrunk=FIRST_TRUNK; iTrunk<=LAST_TRUNK; iTrunk++ ) { sprintf( sText, "entryTrunk(%d)", iTrunk ); iValue = atoi( Tcl_GetVar( pInterp, sText, 0 ) ); ifmem_p->serv_shelf[iTrunk] = iValue;
fprintf( stderr, "card(2)(%d)=%d\n", iTrunk, iValue );
sprintf( sText, "entryTrunkCard(%d)", iTrunk );
iSlot = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
sprintf( sText, "entryTrunkChan(%d)", iTrunk );
iChan = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
if( iSlot == 0 iSlot > 30 )
continue;
if( iChan == 0 iChan > 30 )
continue;
ifmem_p->timesw_in_ctrl[2][iChan] = iTrunk;
fprintf( stderr, "TM2_IN(%d)=%d\n", iChan, iTrunk );
ifmem_p->timesw_out_ctrl[2][iSlot] = iChan;
fprintf( stderr, "TM2_OUT(%d)=%d\n", iSlot, iChan );
ifmem_p->spacesw_ctrl[iChan] = 10;
fprintf( stderr, "SS(%d)=10\n", iChan );
} // end for -- loop through MFSenders
fprintf( stderr, "\n\n" );
for( iSlot=FIRST_MFSEND; iSlot<=LAST_MFSEND; iSlot++ ) { sprintf( sText, "entryMFS(%d)", iSlot ); iValue = atoi( Tcl_GetVar( pInterp, sText, 0 ) ); ifmem_p->serv_shelf[iSlot] = iValue;
fprintf( stderr, "card(2)(%d)=%d\n", iSlot, iValue );
sprintf( sText, "entryMFSTrunk(%d)", iSlot );
iTrunk = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
sprintf( sText, "entryMFSChan(%d)", iSlot );
iChan = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
if( iTrunk <> LAST_TRUNK )
continue;
if( iChan == 0 iChan > 30 )
continue;
ifmem_p->timesw_in_ctrl[2][iChan] = iSlot;
fprintf( stderr, "TM2_IN(%d)=%d\n", iChan, iSlot );
ifmem_p->timesw_out_ctrl[2][iTrunk] = iChan;
fprintf( stderr, "TM2_OUT(%d)=%d\n", iTrunk, iChan );
ifmem_p->spacesw_ctrl[iChan] = 10;
fprintf( stderr, "SS(%d)=10\n", iChan );
} // end for -- loop through MFSenders
// 0 - don't update the MFRs as the code should do it
if( !atoi( argv[1] ) )
return( TCL_OK );
fprintf( stderr, "\n\n" );
for( iSlot=FIRST_MFRCV; iSlot<=LAST_MFRCV; iSlot++ ) { sprintf( sText, "entryMFR(%d)", iSlot ); iValue = atoi( Tcl_GetVar( pInterp, sText, 0 ) ); ifmem_p->serv_shelf[iSlot] = iValue;
fprintf( stderr, "card(2)(%d)=%d\n", iSlot, iValue );
} // end for -- loop through MFSenders
return( TCL_OK );
} // end cmdDoIt
//------------------------------------------------------------
比较TCL/TK 提供的快速而又容易的开发图形拥护界面,X 程序显得很烦琐。Tcl/tk 是一种脚本语言,就象其它的一些脚本语言一样,也有很多事情不能够做或很难做。解决途径是联合 C 与 tcl/tk 一起来开发. tcl/tk 系统提供C 程序调用TCL/TK 的解释器来运行TCL/TK脚本。提供的库包括初始化变量的方法,调用不同的脚本和访问变量。利用这些混合变量对它们访问X固有的特性也提供了好处。简单的回调和时间函数允许程序员制定事件,注册一个C函数为TCL/TK的过程的能力成为一个强大的工具。这篇文档覆盖了TCL/TK脚本与C 集成的一些基础知识。 编译选项部分描述了变量库并包含了建立程序的必要文件。 初始化与注册名令部分解释了怎样开始,怎样从TCL/TK脚本中调用C函数,最后一部分访问变量阐述了怎样来从C函数里来读与写TCL/TK变量。
二、编译选项
为了能访问TCL/TK 库,必须在你的源代码中要设置一些常规的例程做并编译它。有两个调用库的头文件被声明。
#include
#include
编译混合应用程序需要指出正确的编译目录,正确的库,并设置正确的连接标志。在TCL/TK顶部的设置也是必须要包含的文件。而下面的设置是在使用 g++ 时要设置的。你的系统依赖于编译器和文件的定位可能有不同的变化。
-I/software/tcl-7.4/include
-I/software/tk-4.0/include
-I/software/x11r5_dev/Include
-L/software/tcl-7.4/lib
-L/software/tk-4.0/lib
-L/software/x11r5_dev/lib
-ltk
-ltcl
-lX11
三、初始化与注册命令
建立混合 tcl/tk & C 应用程序的中心要围绕几条选择命令。
首先就是"Tk_Main" 函数, 它用来控制整个 tcl/tk 解释器程序。这条命令没有返回值,因此,它需在你的"main" 函数中加下划线,你所有程序的一旦初始化,"Tk_Main" 函数带来三个变量。第二个变量是一个字符串型数组,每个字符串都有一个特殊的含义。第一个变量表示在这个数组的元素个数。第三个变量是指向初始化函数的指针。此初始化函数在许多地方都要被执行。字符串数组通过"Tk_Main"来通知tcl/tk解释器应用程序的名称和tcl/tk 命令在脚本中的位置。这个数组实际上是传给解释器的命令行参数。数组的第一项给出应用程序名称,第二项给出了运行的脚本位置。如果脚本没有在相同的执行目录下,则需要完整路径。由于继承原因,tcl/tk 需要字符串在许多函数里可以修改,它也有函数作用范围的问题,避免这些问题最早的办法是传递时动态分配字符串下面的代码碎片显示了调用 利用"Hello World" 应用程序和脚本"hello.tcl"来调用 "Tk_Main"。
// prototype for the initialization function
int InitProc( Tcl_Interp *interp );
// declare an array for two strings
char ppszArg[2];
// allocate strings and set their contents
ppszArg[0] = (char )malloc( sizeof( char ) 12 );
ppszArg[1] = (char )malloc( sizeof( char ) 12 );
strcpy( ppszArg[0], "Hello World" );
strcpy( ppszArg[1], "./hello.tcl" );
// the following call does not return
Tk_Main( 2, ppszArg, InitProc );
初始化函数
"Tk_Main" 的调用控制了你的程序在tcl/tk中的整个调用,但是在底部初始化之后和tcl/tk 脚本运行之前,能够执行用户自定义的函数。上面的例子中展示了这个类型的函数: "InitProc". 用户定义的初始化函数必须要返回一个整数类型并产生一个指向解释器的参数Tcl_Interp 。在初始化函数里面建立实际解释器调用"Tk_Init"。"Tk_Init"函数设置一个指向解释器的参数,这正是传递到初始化函数的指针。下面的代码仅只是初始化函数,更多的则是在后面列出。
int InitProc( Tcl_Interp *interp )
{
int iRet;
// Initialize tk first
iRet = Tk_Init( interp );
if( iRet != TCL_OK)
{
fprintf( stderr, "Unable to Initialize TK!\n" );
return( iRet );
} // end if
return( TCL_OK );
} // end InitProc
C函数作为 tcl/tk 过程
现在你要熟悉在tcl/tk 脚本中的过程调用。当设计混合应用程序中有tcl/tk的过程调用C函数是可能的。完成它需要调用"Tcl_CreateCommand" 函数。这是在初始化函数里的常用做法。在tcl/tk 过程中调用函数就象调用其它的过程一样。在tcl/tk 脚本中存在就不必声明这个过程。函数注册有一个特定原型的过程。它们必须要返回一个整数类型,并设置4个变量,第一个是tcl/tk库文件类型"ClientData"。第二个变量是指向解释器的指针。最后的两个变量类似于在C "main"函数中的 "argc" 和 "argv" 这两个变量被用于传递参数给tcl/tk 过程。参数"argc" 包含了传递给tcl/tk过程的参数个数"argv" 是字符串数组,每个字符串包含了一个参数。
int Myfunc( ClientData Data, Tcl_Interp *pInterp, int argc, char argv[] );
当一个函数被注册作为tcl/tk 过程使用时需一个指针与之联系,指针通过"ClientData"来传递进来。"ClientData"的概念允许程序员联系数据结构和对象,调用能引用这个对象的过程。这个结构不经常需要。象早先提到的注册过程需要调用"Tcl_CreateCommand" 函数。这个函数有5个参数。第一个参数是指向解释器的指针,第二个参数是在tcl/tk 中的过程名,第三个参数是一个指向函数的指针,它在当tcl/tk过程被执行时调用。最后两个参数是 "ClientData" 项, 一个指针删除例程。它允许C函数在程序退出为了清空联系对象的结构时被调用。象指向删除函数的指针"ClientData"不经常调用。下面是tcl/tk 过程调用"hey_there" 来调用上面声明的"Myfunc"进行注册的例子。
Tcl_CreateCommand( interp, "hey_there", Myfunc, (ClientData)NULL,
(Tcl_CmdDeleteProc )NULL );
变量访问
在执行tcl/tk过程时能调用C函数并允许你从C中获得tcl/tk的帮助,为了从tcl/tk 中获得C的帮助,这有一系列函数,其中包含了从tcl/tk变量中处理获得的信息和设置的信息。
Tcl_GetVar
"Tcl_GetVar" 函数返回一个指向tcl/tk变量的字符串指针。这个函数有三个参数:指向解释器的指针,tcl/tk 变量的名称,一个标志flag。这个变量在执行脚本联系到解释器的当前范围被访问。如果在当前范没有局部变量则访问全局变量。如没有匹配的全局变量存在则返回一个错误。 Flags参数允许你指定TCL_GLOBAL_ONLY, 为了使这个函数仅仅访问此变量名的全局变量,下面是tcl/tk 脚本中被访问的一部分代码。
set say_hello_to "World"
下面的代码是在C里访问tcl/tk变量"say_hello_to".
char sHelloTo[30];
// after this call sHelloTo should contain "World"
strncpy( sHelloTo, Tcl_GetVar( pInterp, "say_hello_to", 0 ), 29 );
Tcl_SetVar
"Tcl_SetVar"函数允许程序员修改tcl/tk变量的值。此函数有四个参数:第一个是解释器指针,第二个是要修改值的tcl/tk变量名称,第三个是要修改的新值,最后一个是tcl/tk标志flags。"Tcl_SetVar" 的标志flags跟"Tcl_GetVar"的相同。当设置期间遇到出错时"Tcl_SetVar"函数返回NULL值。如果变量不存在,则此函数将在解释器指针引用的脚本内建立一个新的变量。下面的代码将设置tcl/tk变量"say_hello_to"的值为"World"。
Tcl_SetVar( pInterp, "say_hello_to", "World", 0 );
集成C & tcl/tk 应用程序的例子
这个应用程序展示了集成C和TCL/TK所需要的基础。此应用程序展示了一系列的登录框和按钮。当信息从登录框输入和按钮被按下时,其他的空域也被相应的更新。这有许多分享内存设备的接口,是调用大型应用程序的方法。这个接口需要头文件在下面没有包含进来,因此不修改而编译此应用程序是不可能的。但就阅读来说这并不是一个坏的示例。
The Makefile
The script file: pr1
The C file: proof.c
#!/.software/local/.admin/bins/bin/wish -f
#============================================================
# xmail
# by Christopher Trudeau, Copyright 1997
#
# This tcl/tk script displays a desktop clock which goes inverse video when
# new mail arrives. A pull down menu allows the user to launch remote login
# sessions on servers specified in the "hosts" variable. The sessions have
# the appropriate "xhost" and "DISPLAY" values.
#
# Comments and criticism on this program are greatly appreciated. Feel free to
# send me a note at ctrudeau@etude.uwaterloo.ca. This material is copyright
# but non-commercial institutes have permission to reproduce the program in
# its entirety, all other uses require explicit written permission of the
# author.
#============================================================
#------------------------------------------------------------
# Global Settings
#-----------------------------------------------------------
# fill in the following list for hosts that you wish to access, spaces or tabs
# separating the names of the hosts; eg:
#
# set hosts "ampere etude watt.uwaterloo.ca"
set hosts "ampere watt ohm morse novice"
#------------------------------------------------------------
# Procedures
#-------------------------------------------------------------
# proc prRefreshDisplay - called periodically to refresh the displayed time and
# status of the mail box
proc prRefreshDisplay {} {
global last
# get the time
set i [exec date]
set i [ string range $i 11 15 ]
# get the mailbox status
catch {[exec frm -q -s new]} mail
if { [string first "no" $mail] == -1 } {
# "You have new mail." results in white on black
.lTime configure -fg white -bg black -text $i
# if first time set, do the double beep thing
if { $last == 0 } {
bell
after 120
bell
}
set last 1
} else {
# "You have no new mail." results in black on white
.lTime configure -fg black -bg white -text $i
set last 0
}
after 15000 prRefreshDisplay
}
#------------------------------------------------------------
# Main Code
#------------------------------------------------------------
# create the main window and place it if specified
wm title . "xmail"
set args [lindex $argv 0]
string trim $args -if
{ $args == "geometry" } {
wm geometry . [lindex $argv 1]
}
# figure out what terminal name we are at
set userName [exec whoami]
set termName [exec who ]
set temp [string first $userName $termName]
set termName [string range $termName $temp end]
set temp [string first ( $termName]
set temp2 [string first ) $termName]
set termName [string range $termName $temp $temp2]
set termName [string trim $termName "()"]
# initialize variables and widgets
set last 0
set font "-*--medium-r-normal--*-120-*--*--*-"
set font2 "-*--medium-r-normal--*-100-*--*--*-"
label .lTime -font $font
# create the menu button
menubutton .mMenu -relief raised -font $font2 -text ">" -menu .mMenu.m
menu .mMenu.m -tearoff 0
.mMenu.m add cascade -label "xterms" -menu .mMenu.m.xterms
#create the sub menu "xterms"
menu .mMenu.m.xterms -tearoff 0
.mMenu.m.xterms add command -label "local" -command {exec xterm -title local &}
set count 0
set hostN [lindex $hosts $count]
while { $hostN != "" } {
catch { exec xhost $hostN }
set cmd "exec rsh $hostN xterm -display $termName:0 -title $hostN &"
.mMenu.m.xterms add command -label $hostN -command $cmd
incr count 1
set hostN [lindex $hosts $count]
}
.mMenu.m add separator
.mMenu.m add command -label "Exit" -command exit
pack .lTime .mMenu -side left
prRefreshDisplay
#-----------------------------------------------------------
CC = gcc
DEPEND = makedepend
TCL_DIR = /software/tcl-7.4
TK_DIR = /software/tk-4.0
INCS = -I$(TCL_DIR)/include -I$(TK_DIR)/include -I/software/x11r5_dev/Include
LIBS = -L/software/x11r5_dev/lib -L$(TCL_DIR)/lib -L$(TK_DIR)/lib
CCFLAGS= $(INCS) $(LIBS) -g -Wall
LFLAGS = -ltk -ltcl -lX11 -lsocket -lm
ALLDEFINES = -DDEBUG
.SUFFIXES: .c .o .cpp
.c.o:
$(CC) $(CCFLAGS) $(ALLDEFINES) -c $< .cpp.o: g++ -g -Wall $(ALLDEFINES) -c $*.cpp PROOF_C = proof.c PROOF_O = proof.o all: proof proof: $(PROOF_O) $(CC) $(CCFLAGS) $(ALLDEFINES) -o $@ $(PROOF_O) $(LFLAGS) clean: rm -f *.o proof core depend:: $(DEPEND) -s "# DO NOT DELETE" -- $(ALLDEFINES) -- $(PROOF_C) # DO NOT DELETE THIS LINE proof.o: /usr/include/stdio.h /usr/include/sys/feature_tests.h proof.o: /usr/include/stdlib.h /usr/include/string.h /usr/include/tcl.h proof.o: /usr/include/tk.h /usr/include/stddef.h /usr/include/sys/types.h proof.o: /usr/include/sys/isa_defs.h /usr/include/sys/machtypes.h proof.o: /usr/include/unistd.h /usr/include/sys/unistd.h ../pbx2.h proof.o: /usr/include/sys/ipc.h /usr/include/sys/msg.h /usr/include/sys/shm.h proof.o: /usr/include/sys/time.h /usr/include/errno.h proof.o: /usr/include/sys/errno.h /usr/include/signal.h proof.o: /usr/include/sys/signal.h ../he2.h #!/.software/local/.admin/bins/bin/wish -f #============================================================ # pr1 # by Christopher Trudeau, Copyright 1997 # # This tcl/tk script is used in conjunction with proof.c to test the hardware # emulator for the SX4 project. # # Comments and criticism on this program are greatly appreciated. Feel free to # send me a note at ctrudeau@etude.uwaterloo.ca. This material is copyright # but non-commercial institutes have permission to reproduce the program in # its entirety, all other uses require explicit written permission of the # author. #============================================================ wm title . "Proof" #============================================================ # main window declarations #============================================================ # create the frames for each row of entry fields for {set i 0} {$i <>
#include
#include
#include
#include
#include
#include
#include "../pbx2.h"
#include "../he2.h"
//----------------------------------------------------------
// Global Variables
struct ifmem_t ifmem_p; // pointer to shared hardware memory
//-----------------------------------------------------------
// Function Prototypes
int InitProc(Tcl_Interp interp);
int cmdDoIt( ClientData clientData, Tcl_Interp *pInterp, int argc,
char *argv[] );
//-----------------------------------------------------------
int main()
{
char ppszArg[2];
int iMemId;
printf( "Starting proof...\n" );
// get pointer to shared interface memory
iMemId = shmget( IFMEM_KEY, sizeof( struct ifmem_t ), 0644);
ifmem_p = (struct ifmem_t )shmat( iMemId, 0, 0);
if( (int)ifmem_p == -1 )
{
printf( "Error: unable to access shared interface memory\n" );
exit( 0 );
} // end if -- failed to get interface memory
// initialize arguments for Tk_Main
ppszArg[0] = (char )malloc( sizeof( char ) 8 );
ppszArg[1] = (char )malloc( sizeof( char ) 65 );
strcpy( ppszArg[0], "proof" );
strcpy( ppszArg[1], "/home3/ctrudeau/s/tcl/proof/pr1" );
printf( "Executing tcl/tk script\n" );
Tk_Main( 2, ppszArg, InitProc );
return( 0 );
} // end main
//-----------------------------------------------------------
int InitProc( Tcl_Interp interp )
{
int iRet;
// Initialize tk first
iRet = Tk_Init( interp );
if( iRet != TCL_OK)
{
printf( "Unable to Initialize TK!\n" );
return( iRet );
} // end if
// register any new tcl/tk commands
Tcl_CreateCommand( interp, "cmdDoIt", cmdDoIt, (ClientData)NULL,
(Tcl_CmdDeleteProc )NULL );
return( TCL_OK );
} // end InitProc
//-----------------------------------------------------------
// cmdDoIt
//
// This function is called as a command from tcl/tk. It is issued when the
// user pushes the "Do it" button. Each of the entry fields is checked
// for their contents and the interface memory is updated accordingly.
// The update to i/f mem is used to make connections between various cards
// and to put values into those cards (digits, loop back bits, etc)
//
int cmdDoIt( ClientData clientData, Tcl_Interp *pInterp, int argc,
char *argv[] )
{
int iSlot, iValue, iTrunk, iChan;
char sText[64];
fprintf( stderr, "****** Doing it\n" );
for( iTrunk=FIRST_TRUNK; iTrunk<=LAST_TRUNK; iTrunk++ ) { sprintf( sText, "entryTrunk(%d)", iTrunk ); iValue = atoi( Tcl_GetVar( pInterp, sText, 0 ) ); ifmem_p->serv_shelf[iTrunk] = iValue;
fprintf( stderr, "card(2)(%d)=%d\n", iTrunk, iValue );
sprintf( sText, "entryTrunkCard(%d)", iTrunk );
iSlot = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
sprintf( sText, "entryTrunkChan(%d)", iTrunk );
iChan = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
if( iSlot == 0 iSlot > 30 )
continue;
if( iChan == 0 iChan > 30 )
continue;
ifmem_p->timesw_in_ctrl[2][iChan] = iTrunk;
fprintf( stderr, "TM2_IN(%d)=%d\n", iChan, iTrunk );
ifmem_p->timesw_out_ctrl[2][iSlot] = iChan;
fprintf( stderr, "TM2_OUT(%d)=%d\n", iSlot, iChan );
ifmem_p->spacesw_ctrl[iChan] = 10;
fprintf( stderr, "SS(%d)=10\n", iChan );
} // end for -- loop through MFSenders
fprintf( stderr, "\n\n" );
for( iSlot=FIRST_MFSEND; iSlot<=LAST_MFSEND; iSlot++ ) { sprintf( sText, "entryMFS(%d)", iSlot ); iValue = atoi( Tcl_GetVar( pInterp, sText, 0 ) ); ifmem_p->serv_shelf[iSlot] = iValue;
fprintf( stderr, "card(2)(%d)=%d\n", iSlot, iValue );
sprintf( sText, "entryMFSTrunk(%d)", iSlot );
iTrunk = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
sprintf( sText, "entryMFSChan(%d)", iSlot );
iChan = atoi( Tcl_GetVar( pInterp, sText, 0 ) );
if( iTrunk <> LAST_TRUNK )
continue;
if( iChan == 0 iChan > 30 )
continue;
ifmem_p->timesw_in_ctrl[2][iChan] = iSlot;
fprintf( stderr, "TM2_IN(%d)=%d\n", iChan, iSlot );
ifmem_p->timesw_out_ctrl[2][iTrunk] = iChan;
fprintf( stderr, "TM2_OUT(%d)=%d\n", iTrunk, iChan );
ifmem_p->spacesw_ctrl[iChan] = 10;
fprintf( stderr, "SS(%d)=10\n", iChan );
} // end for -- loop through MFSenders
// 0 - don't update the MFRs as the code should do it
if( !atoi( argv[1] ) )
return( TCL_OK );
fprintf( stderr, "\n\n" );
for( iSlot=FIRST_MFRCV; iSlot<=LAST_MFRCV; iSlot++ ) { sprintf( sText, "entryMFR(%d)", iSlot ); iValue = atoi( Tcl_GetVar( pInterp, sText, 0 ) ); ifmem_p->serv_shelf[iSlot] = iValue;
fprintf( stderr, "card(2)(%d)=%d\n", iSlot, iValue );
} // end for -- loop through MFSenders
return( TCL_OK );
} // end cmdDoIt
//------------------------------------------------------------
Subscribe to:
Posts (Atom)